Beruflich Dokumente
Kultur Dokumente
¿Qué es Spring?
Core Container
Un ejemplo de instancia del contenedor podría ser el siguiente, donde al constructor se le pasa
como argumento el archivo XML donde se definen los beans y sus dependencias:
Si no se quiere tener toda la definición de beans en un único archivo XML se puede utilizar la
etiqueta import. Ejemplo:
<beans>
<import resource="services.xml"/>
<import resource="resources/messageSource.xml"/>
<import resource="/resources/themeSource.xml"/>
El objeto cliente para acceder al servicio primero lo identifica y después lo localiza por medio de
una referencia (en el ejemplo la referencia es "ob"); a continuación hace la llamada al
procedimiento. El inconveniente es el acoplamiento entre la capa cliente (front), la capa de
acceso al servicio y la implementación del servicio.
Spring se identifica con una forma de IoC denominada Inyección de Dependencia, en la que
el servicio se identifica y localiza por medio de mecanismos no programáticos, externos
al código, como por ejemplo un archivo XML. Las dependencias con respecto a los
servicios son explicitas y no están en el código. Con ello se gana en facilidad de test y
mantenimiento. Algunas formas de inyección de dependencia son:
• Inyección por medio de métodos Setter. En nuestro ejemplo anterior de Spring la clase
SpringappController tiene un método setLibreria(), que es el que utiliza el framework
para inyectar la propiedad "libreria".
• Inyección por medio de constructor. Por ejemplo, el bean "prod_02" tiene un
constructor que admite dos argumentos:
•
• <bean id="prod_02" class="org.ejemplo.Producto">
• <constructor-arg value="AK" />
• <constructor-arg value="600" />
• </bean>
Descarga de Spring
Para utilizar Spring en nuestro proyecto simplemente tenemos que instalar la librería
spring.jar, que exige además la librería de log básico: commons-logging-api.jar,
normalmente incluida en la distribución de Tomcat. Si utilizamos Eclipse tenemos que hacer
Project - Properties - Java Build Path - Add Jars. Además se puede señalar el "JavaDoc
Location" (donde se encuentran los JavaDoc de spring.jar), estos JavaDocs se incluyen
también en el archivo descargado. Ejemplo de "JavaDoc Location": file:/C:/DOC/Spring/spring-
framework-2.0.7/docs/api/.
Ejemplo de Spring
Introducción
Vamos a poner en práctica los conceptos fundamentales del "Core Container" de Spring.
Empezaremos creando el proyecto con Eclipse e instalando las librerías. Para simplificar
vamos a empezar con un proyecto Java no Web (una sencilla aplicación "standalone") al que
se añaden las librerías necesarias (ver la Introducción).
Las clases del dominio de problema tienen los típicos métodos set y get. Además tienen un
método toString() que devuelve una cadena que representa los atributos de cada objeto. El
cliente tiene unicamente un NIF:
package org.ejemplo;
public class Cliente {
public Cliente() {}
La clase Producto:
package org.ejemplo;
import java.util.Enumeration;
import java.util.Properties;
public Producto() {}
return str.toString();
}
}
package org.ejemplo;
import java.util.*;
/
**********************************************************************
********************
* La factura tiene un cliente y una lista de productos.
* Lanza excepción si no hay un cliente definido o si la lista de
productos está vacia.
* Ver métodos set correspondientes.
*********************************************************************
********************/
public class Factura {
protected Cliente cliente;
protected int numero;
protected List productos;
package org.ejemplo;
import org.springframework.beans.factory.xml.XmlBeanFactory;
//import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.ClassPathResource;;
/
**********************************************************************
******************
* Clases del dominio: Factura, Producto y Cliente. La factura tiene
asociado un cliente y
* una lista de productos.
* Son necesarias las librerias spring.jar y commons-logging-api.jar
*********************************************************************
******************/
public class Inicio {
}
catch (Exception e) {
e.printStackTrace();
}
}
}
Con el metodo getBean() se puede obtener un bean del contenedor. Como argumento se pasa
el id del bean, definido en el archivo XML. En este ejemplo se obtiene la factura y se muestran
por pantalla sus propiedades, obtenidas por medio del método toString().
</beans>
Internacionalización
Introducción
Resulta interesante tener todos los mensajes (normales o de error) en un archivo properties,
además sería útil que tuviésemos un archivo properties por cada idioma, de tal forma que se
cargase de manera automática el archivo properties de mensajes en función del idioma
escogido en el sistema. En nuestro ejemplo pondremos en el directorio bin los siguientes
archivos:
Si estuvieramos en un proyecto web, el directorio activo para los archivos properties es WEB-
INF/classes. En nuestro ejemplo, como es una sencilla aplicación de consola, el directorio
activo es bin.
En el archivo xml de definición de contexto tenemos que indicar la creación de un bean del
tipo MessageSource
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource
">
<property name="basenames">
<list>
<value>mensajes/textos</value>
<value>mensajes/errores</value>
</list>
</property>
</bean>
En resumen, MessageSource cumple con las reglas del ResourceBundle del JDK.
Spring AOP
1. Introducción
El contenedor IoC de Spring no depende de la implementación AOP, por tanto el uso de AOP
no es obligado.
Las capacidades AOP que vamos a utilizar están cubiertas por la librería spring.jar.
2. Un sencillo ejemplo
Supongamos una empresa que quiere gestionar la asignación de recursos (por ejemplo
paquetes) a otros recursos (por ejemplo camiones). Tenemos el interfaz Recurso,
implementado por RecursoGeneral. Las ordenes de asignar o liberar recursos llegan a
GestorRecursos, que implementa el interface Gestor. Con esta situación podemos hacer algo
como:
Se puede ver que el ejemplo es desde el punto de vista de implementación muy sencillo.
3. Complicando el ejemplo
Algunas consideraciones:
Una forma de implementar un aspecto en AOP es crear un Advice, que se dispara cada vez
que hay una llamada al objeto destino (target object). En nuestro caso, después de cada
llamada a GestorRecursos se invocará a ControlOperaciones.afterReturning().
ControlOperaciones implementa el interface de Spring AOP AfterReturningAdvice, cuyo único
método es afterReturning(). El Advice se comporta "como un" interceptor posterior a la
invocación al objeto destino. Definición Spring: un Advice es una acción que realiza un
aspecto. Cuando hablamos de joint point nos referimos a la unión que se establece entre un
punto de ejecución (un método) del objeto destino (por ejemplo, el método liberar()) y un
Advice.
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
Diagrama de secuencia:
El diagrama de secuencia muestra lo siguiente:
6. Application-context y cliente
El primer objeto es el proxy, que hemos denominado proxyGestor. Una de sus propiedades es
el target object, del tipo GestorRecursos, que es creado por Spring. Más adelante, cuando
veamos el código fuente del cliente podremos observar que el cliente no instancia ni al proxy ni
al target object.
Otra propiedad del proxy es el Advice (interceptorNames), que también es instanciado por
Spring.
Existen dos tipos de proxy. En este ejemplo se usa el proxy Spring. Si queremos un proxy
JDK, que utiliza java.lang.reflect.Proxy, debemos indicar la propiedad proxyInterfaces, que en
nuestro ejemplo la hemos puesto en comentario.
<beans>
</beans>
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
}
catch (Exception e) {
e.printStackTrace();
}
}
}
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
• Around advice: también conocido como método interceptor, recibe el control tanto
antes como después del punto de ejecución (o joint point, por ejemplo asignar())
• Before advice, recibe el control antes de la ejecución del joint point
• After advice, ya lo hemos visto en el ejemplo, después del punto de ejecución
• Throws advice, después del punto de ejecución, si hay excepción
La salida por pantalla muestra que primero realiza el método correspondiente (liberar() o
asignar()) y después afterReturning:
Spring AOP
Nota previa
Los siguientes ejemplos de código siguen el ejemplo anterior basado en un objeto destino
(target object) denominado RecursoGeneral, que implementa el interface Recurso.
Un aspecto establece joint points (puntos de unión), que son los puntos de ejecución del
objeto destino (por ejemplo, GestorRecursos.liberar()) que entran dentro del aspecto. O dicho
de otro modo, los joint point son los puntos de ejecución del objeto destino que producen una
invocación a un Advice.
Un punto de corte no es más que una etiqueta o predicado que asocia (match):
• Advice
• Puntos de unión (Joint points)
Un jointcut no es mas que una etiqueta que establece una relación entre puntos de ejecución
del objeto destino y un Advice. Por tanto con un punto de corte somos selectivos, podemos
escoger los puntos de ejecución en el objeto destino y su correspondiente Advice.
<bean id="controladorAsignacion"
class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"
>
<property name="mappedName" value="asignar"/>
<property name="advice" ref="controlador"/>
</bean>
Around Advice
Around advice: también conocido como método interceptor, recibe el control tanto antes
como después del punto de ejecución (o joint point, por ejemplo nuestro método asignar()).
3. El método invoke() del Advice debe llamar o invocar al objeto destino con:
4.
5. invocation.proceed();
En el application-context tenemos dos advices, uno para el Joint Point liberar() y otro para el
Joint Point asignar(). El advice de liberar() es un Around Advice, que implementa
MethodInterceptor:
<beans>
<bean id="proxyGestor"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<bean class="org.ejemplo.GestorRecursos">
</bean>
</property>
<property name="interceptorNames">
<list>
<idref bean="controladorAsignacion" />
<idref bean="controladorLiberacion" />
</list>
</property>
</bean>
<bean id="controladorAsignacion"
class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"
>
<property name="mappedName" value="asignar"/>
<property name="advice" ref="controlador1"/>
</bean>
<bean id="controlador1" class="org.ejemplo.ControlOperaciones">
</bean>
<bean id="controladorLiberacion"
class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"
>
<property name="mappedName" value="liberar"/>
<property name="advice" ref="controlador2"/>
</bean>
<bean id="controlador2"
class="org.ejemplo.ControlOperacionesAround">
</bean>
</beans>
invocacion.getThis().getClass().getName() );
System.out.println( " Clase que representa la
invocación: " +
invocacion.getClass().getName() );
System.out.println( " Método interceptado: " +
invocacion.getMethod().getName() );
System.out.print( " Argumentos: " );
for ( Object obj: invocacion.getArguments() )
System.out.print( obj.toString() + "("+
obj.getClass().getName()+") ");
System.out.println( "");
try {
return invocacion.proceed(); // Llamada al
objeto destino (joint point)
}
finally {
System.out.println( "Ya se ha ejecutado el
Joint Point");
}
}
}
Introducción
JDBC es el API Java para el acceso a base de datos. Es un solución difícil debido varios
inconvenientes o limitaciones:
Todo lo anterior hace que una sólución basada en el puro JDBC resulte difícil de mantener. Por
ello se ha popularizado el uso de frameworks como iBatis o Hibernate. Spring puede trabajar
con otros frameworks, fiel a su filosofía no invasiva.
El pool de conexiones
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/basedatos?
autoReconnect=true
jdbc.username=user
jdbc.password=pass
El archivo de propiedades se carga como un bean ("location" hace referencia al lugar del
archivo properties):
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderCon
figurer">
<property name="location">
<value>/WEB-INF/jdbc.properties</value>
</property>
</bean>
Este código inyecta las properties (cfg) en la factoria, cuando se han cargado las definiciones
de bean (pero todavía no se han instanciado).
Introducción al ejemplo
Arquitectura:
• Vista:
o HTML y JSP: index.html enlaza con jsp/index.jsp, esta incluye
jsp/formulario.jsp. En index.jsp se invoca a la carga del contexto Spring y de las
properties. Las JSP usan Java Beans
o Java Beans: se utilizan para generar HTML y sirven de intermediarios con el
DAO. Se encuentran en com.etiquetas. Cuando trabajemos con Spring MVC
estos Java Beans no serán necesarios.
• Acceso a datos (con Spring):
o El dao está en com.persistencia. Se instancia en index.jsp
o Como auxiliar del dao está el mapper (com.persistencia.mappers) que asocia
atributos del objeto Cliente con columnas de la tabla
o Como auxiliar del dao para la construcción de query tenemos el paquete
com.persistencia.query
• Servicios de carga de contexto y recursos (Spring):
o com.recursos.GestorCarga es la clase que en su constructor realiza esta labor.
o GestorCarga crea la factoria Spring
o GestorCarga se maneja como Java Bean desde index.jsp. En Spring MVC la
carga de contexto se automatiza y no resulta necesario este servicio de
carga
• Dominio:
o com.dominio.Cliente
Este ejemplo muestra una página muy densa (diversos SELECT, formulario, etc.), por
razones pedagógicas. Un caso real trataría de separar las consultas más costosas
(SELECTS) del formulario.
import
org.springframework.beans.factory.config.PropertyPlaceholderConfigurer
;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import com.persistencia.*;
/
**********************************************************************
**************
* Carga del archivo de contexto y del archivo de properties
*********************************************************************
****************/
public class GestorCarga {
private XmlBeanFactory factory;
private PropertyPlaceholderConfigurer cfg;
private DAOClienteSpring dao;
/**************************************************************
*****************
* Inyecta las properties (cfg) en la factoria, cuando se han
cargado las
* definiciones de bean, pero todavía no se han instanciado
**********************************************************************
*********/
public GestorCarga() throws Exception {
factory = new XmlBeanFactory(new
ClassPathResource("../application-context.xml"));
cfg = new PropertyPlaceholderConfigurer();
cfg.setLocation(new
ClassPathResource("../configuracion.properties"));
cfg.postProcessBeanFactory(factory);
dao = (DAOClienteSpring)
factory.getBean( "DAOCliente");
}
El archivo de definición del contexto de aplicación tiene únicamente dos bean: uno para la
fuente de datos (Data Base Common Pooling, dbcp, de Apache) y otro para el DAO (observar
que un atributo del DAO es la fuente de datos):
El dominio
Es algo sencillo, la clase que representa al cliente (el típico bean asociado a tabla):
package com.dominio;
Persistencia
Si se hecha un rápido vistazo al DAO se puede ver que se usa con frecuencia
org.springframework.jdbc.core.simple.SimpleJdbcTemplate. Una de las clases más
importantes de Spring 2.0 para encapsular la complejidad de usar JDBC. En un ejemplo típico
creamos el SimpleJdbcTemplate (pasandole el DataSource de JdbcDaoSupport) y ejecutamos
una SELECT por medio de query().
El segundo argumento de query() es un mapper, del que hablaremos más adelante. El código
del DAO:
package com.persistencia;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import java.util.List;
import com.persistencia.mappers.ClienteMapper;
import com.persistencia.query.ClientePorEdadSetter;
import com.dominio.Cliente;
/
**********************************************************************
***
* DAO que emplea la clase JdbcDaoSupport de Spring
*********************************************************************
****/
public class DAOClienteSpring extends JdbcDaoSupport {
/**************************************************************
***********
* Inicializador de DAO
**********************************************************************
***/
protected void initDao() throws Exception {
super.initDao();
}
/**************************************************************
***********
* Count de clientes. Usa SimpleJdbcTemplate (Spring 2.0)
**********************************************************************
***/
public int getNumeroClientes() {
return new
SimpleJdbcTemplate(getDataSource()).queryForInt("SELECT COUNT(*) FROM
cliente");
}
/**************************************************************
***********
* Count de clientes entre un rango de edades. Usa
SimpleJdbcTemplate (Spring 2.0)
**********************************************************************
***/
public int getNumeroClientes(Integer edadMinima, Integer
edadMaxima) {
return new
SimpleJdbcTemplate(getDataSource()).queryForInt(
"SELECT count(*) FROM cliente WHERE edad
>= ? AND edad <= ?",
edadMinima, edadMaxima );
}
/**************************************************************
***********
* Select de todos los clientes. Usa un mapper. Usa
SimpleJdbcTemplate (Spring 2.0)
**********************************************************************
***/
public List<Cliente> selectAll() {
SimpleJdbcTemplate template = new
SimpleJdbcTemplate(getDataSource());
return template.query("SELECT * FROM cliente", new
ClienteMapper());
}
/**************************************************************
***********
* Select de los clientes entre un rango de edades. Usa mapper
* Usa PreparedStatement
**********************************************************************
***/
public List<Cliente> selectAllPreparedStatement(int
edadMinima, int edadMaxima) {
return getJdbcTemplate().query( "SELECT * FROM cliente
WHERE edad >= ? AND edad <= ?",
new ClientePorEdadSetter(edadMinima,
edadMaxima),new ClienteMapper());
}
/**************************************************************
***********
* Select de UN cliente. Usa un mapper. Usa SimpleJdbcTemplate
(Spring 2.0)
**********************************************************************
***/
public Cliente selectCliente( String codigo ) {
SimpleJdbcTemplate template = new
SimpleJdbcTemplate(getDataSource());
return (Cliente) template.queryForObject("SELECT * FROM
cliente WHERE codigo=?",
new ClienteMapper(), codigo);
}
/**************************************************************
***********
* Select para funciones mátemáticas básicas
**********************************************************************
***/
public long getEdadTotal() {
return getJdbcTemplate().queryForLong("SELECT SUM(edad)
FROM cliente");
}
public long getEdadMedia() {
return getJdbcTemplate().queryForLong("SELECT AVG(edad)
FROM cliente");
}
public long getEdadMaxima() {
return getJdbcTemplate().queryForLong("SELECT MAX(edad)
FROM cliente");
}
public long getEdadMinima() {
return getJdbcTemplate().queryForLong("SELECT MIN(edad)
FROM cliente");
}
/**************************************************************
***********
* Insert de cliente con SimpleJdbcTemplate (Spring 2.0)
* Usa ClienteMapper para inyectar los datos en la sentencia
SQL
**********************************************************************
***/
public int insertarCliente(Cliente cliente) {
SimpleJdbcTemplate template = new
SimpleJdbcTemplate(getDataSource());
return template.update("INSERT INTO cliente (codigo,
nombre, ape1, ape2, edad) " +
"VALUES (?, ?, ?, ?, ?)",
ClienteMapper.mapAributos(cliente) );
}
/**************************************************************
***********
* Actualiación de cliente con SimpleJdbcTemplate (Spring 2.0)
* Usa ClienteMapper para inyectar los datos en la sentencia
SQL
**********************************************************************
***/
public int actualizarCliente(Cliente cliente) {
SimpleJdbcTemplate template = new
SimpleJdbcTemplate(getDataSource());
return template.update("UPDATE cliente SET codigo=?,
nombre=?, ape1=?, "+
"ape2=?, edad=? WHERE codigo='" +
cliente.getCodigo() + "'",
ClienteMapper.mapAributos(cliente));
}
/**************************************************************
***********
* Borrado de cliente con SimpleJdbcTemplate (Spring 2.0)
**********************************************************************
***/
public int borrarCliente(Cliente cliente) {
SimpleJdbcTemplate template = new
SimpleJdbcTemplate(getDataSource());
return template.update("DELETE FROM cliente WHERE
codigo=?", cliente.getCodigo());
}
/**************************************************************
***********
* En función del argumento realiza una operación de
actualización de datos
**********************************************************************
***/
public int updateHub(String operacion, Cliente cliente) {
if ( operacion.compareTo( "Insertar") == 0 )
return insertarCliente( cliente );
if ( operacion.compareTo( "Actualizar") == 0 )
return actualizarCliente( cliente );
if ( operacion.compareTo( "Borrar") == 0 )
return borrarCliente( cliente );
return 0;
}
}
package com.persistencia.mappers;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.simple.ParameterizedRowMapper;
import com.dominio.Cliente;
/
**********************************************************************
***
* Mapea atributos del objeto y columnas de la base de datos
*
* El metodo mapRow() inyecta en el resultSet los datos del objeto
Cliente.
* Es un callback: llamado por Spring por cada fila del resultSet.
* Tiene como argumentos dicho resultSet y el nº de fila
*
* El método mapAtributos devuelve los atributos del objeto cliente,
* en la forma de un array de objetos
*********************************************************************
****/
public class ClienteMapper implements ParameterizedRowMapper {
public Cliente mapRow(ResultSet resultSet, int row) throws
SQLException {
Cliente cli = new Cliente();
cli.setCodigo(resultSet.getString("codigo"));
cli.setNombre(resultSet.getString("nombre"));
cli.setApe1(resultSet.getString("ape1"));
cli.setApe2(resultSet.getString("ape2"));
cli.setEdad(resultSet.getInt("edad"));
return cli;
}
static public Object[] mapAributos( Cliente cliente) {
return new Object[] {
cliente.getCodigo(),
cliente.getNombre(),
cliente.getApe1(),
cliente.getApe2(),
cliente.getEdad()};
}
}
Setter
import java.sql.SQLException;
import org.springframework.jdbc.core.PreparedStatementSetter;
import java.sql.PreparedStatement;
/
**********************************************************************
***
Clase que inyecta parametros en un PreparedStatement
*********************************************************************
****/
public class ClientePorEdadSetter implements PreparedStatementSetter
{
private int edadMinima;
private int edadMaxima;
<html>
<%
int edadMinima = 25;
int edadMaxima = 35;
%>
<CENTER><H4><b>Gestión de clientes</H4></b></CENTER>
<!--
El objeto select muestra resultados de consultas de clientes.
Para ello usa el DAO. Se le pasa un rango de edades para los
select con where
-->
<jsp:useBean id="select" scope="request"
class="com.etiquetas.ClienteEtiquetaSelect">
<jsp:setProperty name="select" property="dao" value="<
%=gestorCarga.getDao()%>"/>
<jsp:setProperty name="select" property="edadMinima"
value="25"/>
<jsp:setProperty name="select" property="edadMaxima"
value="35"/>
</jsp:useBean>
</TD></TR></TABLE>
<p>Las operaciones de Insertar, Actualizar, Borrar y Consultar se
refieren
siempre a un <b>código de registro</b>, que es clave primaria.</p>
<p><b>Actualizar</b>: update del registro cuyo código
corresponda con el
de la página.</p>
<p><b>Consultar</b>: select del registro cuyo código corresponda
con el
de la página.</p>
<p><b>Refrescar</b>: vuelve a presentar la página con listados
actualizados.</p>
El Java Bean "select" se usa para aligerar de Java las páginas JSP. El bean devuelve un
String que se vuelca en la página. De esta forma se obtienen los listados de clientes. Más
adelante, cuando estudiemos Spring MVC veremos que este uso de los JavaBeans no es
necesario; ya que los controladores devolverán a las páginas los objetos que deben
mostrar..
package com.etiquetas;
import com.dominio.Cliente;
import com.persistencia.DAOClienteSpring;
/
**********************************************************************
*************
* JavaBean generador de código html.
* Es un intermediario entre la página jsp y el dao: la página llama a
este JavaBean
* y el JavaBean devuelve el resultado de las consultas como tipo
String
*********************************************************************
*************/
public class ClienteEtiquetaSelect {
DAOClienteSpring dao; // El DAO
Integer edadMinima; // Para un where
por edad
Integer edadMaxima; // Para un where
por edad
String inicioLinea = "<li>"; // Etiqueta para iniciar linea
de select
String finLinea = "</li>"; // Etiqueta para
terminar linea de select
/**************************************************************
*********************
* Devuelve el nº total de clientes
**********************************************************************
************/
public String getCuenta() {
return ""+dao.getNumeroClientes();
}
/**************************************************************
*********************
* Devuelve el nº de clientes entre las edades
**********************************************************************
************/
public String getCuentaSelectiva() {
return ""+dao.getNumeroClientes(edadMinima,edadMaxima);
}
/**************************************************************
*********************
* Devuelve todos los clientes. Para separar líneas usa los
atributos
* incioLinea y finLinea
**********************************************************************
************/
public String getSelect() {
StringBuffer str = new StringBuffer();
for ( Cliente cliente : dao.selectAll() )
str.append( inicioLinea + cliente.getCodigo() +
", " +
cliente.getNombre() + " (" +
cliente.getEdad() +")"+finLinea);
return str.toString();
}
/**************************************************************
*********************
* Devuelve los clientes entre las edades. Para separar líneas
usa los atributos
* incioLinea y finLinea
**********************************************************************
************/
public String getSelectPorEdad() {
StringBuffer str = new StringBuffer();
for ( Cliente cliente :
dao.selectAllPreparedStatement(edadMinima,edadMaxima) )
str.append( inicioLinea + cliente.getCodigo() +
", " +
cliente.getNombre() + " (" +
cliente.getEdad() + ")"+finLinea);
return str.toString();
}
/**************************************************************
*********************
* Definir periodo de edad
**********************************************************************
************/
public void setEdadMaxima(Integer edadMaxima) {
this.edadMaxima = edadMaxima;
}
public void setEdadMinima(Integer edadMinima) {
this.edadMinima = edadMinima;
}
/**************************************************************
*********************
* Definir inicio y fin de linea
**********************************************************************
************/
public void setFinLinea(String finLinea) {
this.finLinea = finLinea;
}
public void setInicioLinea(String inicioLinea) {
this.inicioLinea = inicioLinea;
}
Introducción
Algunas aplicaciones requieren el manejo de documentos. Por ejemplo una Web que gestiona
ofertas de trabajo puede solicitar al candidato que cargue (upload) un archivo con su CV y le
permite a la empresa ofertante descargarse (download) dicho archivo. Los documentos se
almacenan en campos de tipo LOB (Large Object) en nuestra base de datos. Si el campo
soporta gran cantidad de datos en modo caracter hablamos de CBLOB y si soporta datos
binarios nos refereimos BLOB. Los LOBs son campos que exigen un tratamiento especial en
JDBC y por tanto en Spring.
Insert
Spring exige
import com.persistencia.query.ImagenSetter;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import java.io.IOException;
import java.io.InputStream;
...
public void insertarImagen(Integer id, final InputStream in)
throws IOException {
LobHandler lobHandler = new DefaultLobHandler();
import
org.springframework.jdbc.core.support.AbstractLobCreatingPreparedState
mentCallback;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.jdbc.support.lob.LobCreator;
import java.io.IOException;
import org.springframework.dao.DataAccessException;
import java.io.InputStream;
import java.sql.PreparedStatement;
import java.sql.SQLException;
El método setValues() ya lo hemos visto como la solución Spring para trabajar con
PreparedStatement. Es un método CallBack, es decir, será llamado por Spring, no por nuestra
aplicación, cuando sea necesario inyectar los parámetros en la sentencia INSERT:
• Para inyectar el segundo parámetro usamos un objeto del tipo LobCreator, que nos da
Spring como una utilidad para el manejo de LOBs. Este objeto tiene el método
setBlobAsBinaryStream para que un stream de entrada se vuelque sobre un campo
BLOB. Otros métodos de LobCreator: setClobAsString(), setBlobAsBytes(), etc.
Argumentos de setBlobAsBinaryStream:
Select
Introducción
• Una capa de vista, formada de jsp, html, css, etiquetas personalizadas, etc.
• Una capa de modelo, que cuenta con las subcapas de servicios, persistencia (daos,
etc.) y dominio (beans). Se forma mediante clases e interfaces Java.
Lo que devuelve cada controlador es un objeto del tipo ModelAndView. Este objeto se compone
de:
Configuración: web.xml
Empezaremos con el clásico web.xml. En el que se empieza definiendo el Listener que ante el
evento contextInitialized cargará el contexto de aplicación:
<listener>
<listener-
class>org.springframework.web.context.ContextLoaderListener</listener-
class>
</listener>
<servlet>
<servlet-name>spring21</servlet-name>
<servlet-
class>org.springframework.web.servlet.DispatcherServlet</servlet-
class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring21</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
2. El url-pattern indica los tipos de peticiones que aceptará, en nuestro ejemplo con las
extensiones .do.
Configuración: spring-servlet.xml
class="org.springframework.web.servlet.view.InternalResourceViewResolv
er">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
En nuestro ejemplo tenemos dos controladores. Uno que hace las inserciones,
actualizaciones (update) y consultas; otro que hace el borrado. Lo veremos más adelante.
Normalmente un formario está asociado a un controlador, señalando la asociación en el action
del formulario. En nuestro ejemplo cada controlador tiene como atributo un bean
servicioCliente, que es definido en el applicationContext.xml.
El viewResolver indica donde están las vistas (normalmente JSPs) que son invocadas por el
controlador frontal. En el ejemplo las vistas se toman de /WEB-INF/jsp y tienen la extensión jsp.
Configuración: applicationContext.xml
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
-->
<beans>
<!-- Las propiedades del dataSource tienen como valor
properties -->
<bean id="dataSource"
class="org.apache.tomcat.dbcp.dbcp.BasicDataSource" destroy-
method="close">
<property name="driverClassName" value="$
{jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- El DAO que tiene como atributo el dataSource -->
<bean id="DAOCliente"
class="com.persistencia.DAOClienteSpring">
<property name="dataSource" ref="dataSource"/>
</bean>
class="org.springframework.beans.factory.config.PropertyPlaceholderCon
figurer">
<property name="locations">
<list>
<value>WEB-
INF/configuracion.properties</value>
</list>
</property>
</bean>
</beans>
o El dataSource tiene como atributos datos que son definidos por un archivo
properties. Por ejemplo, jdbc.url se refiere a
jdbc:mysql://localhost:3306/proactiv_prueba?autoReconnect=true. En el
archivo configuracon.properties del directorio WEB-INF se encuentra la
siguiente definición:
o
o jdbc.driverClassName=com.mysql.jdbc.Driver
o
jdbc.url=jdbc:mysql://localhost:3306/proactiv_prueba?
autoReconnect=true
o jdbc.username=usuario
o jdbc.password=password
En resumen:
• Los controladores (en spring21-servlet.xml) hacen referencia al servicio
servicioCliente.
• En applicationContext.xml se puede ver que el servicioCliente tiene como atributo el
DAO
• El DAO tiene como atributo el dataSource, que se configura por un archivo
properties.
Introducción al ejemplo
<listener>
<listener-
class>org.springframework.web.context.ContextLoaderListener</listene
r-class>
</listener>
<servlet>
<servlet-name>spring21</servlet-name>
<servlet-
class>org.springframework.web.servlet.DispatcherServlet</servlet-
class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring21</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
class="org.springframework.web.servlet.view.InternalResourceViewResolv
er">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
En nuestro ejemplo tenemos dos controladores. Uno que hace las inserciones,
actualizaciones (update) y consultas; otro que hace el borrado. Lo veremos más adelante.
Normalmente un formario está asociado a un controlador, señalando la asociación en el action
del formulario. En nuestro ejemplo cada controlador tiene como atributo un bean
servicioCliente, que es definido en el applicationContext.xml.
<beans>
<!-- Las propiedades del dataSource tienen como valor
properties -->
<bean id="dataSource"
class="org.apache.tomcat.dbcp.dbcp.BasicDataSource" destroy-
method="close">
<property name="driverClassName" value="$
{jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- El DAO que tiene como atributo el dataSource -->
<bean id="DAOCliente"
class="com.persistencia.DAOClienteSpring">
<property name="dataSource" ref="dataSource"/>
</bean>
class="org.springframework.beans.factory.config.PropertyPlaceholderCon
figurer">
<property name="locations">
<list>
<value>WEB-
INF/configuracion.properties</value>
</list>
</property>
</bean>
</beans>
o El dataSource tiene como atributos datos que son definidos por un archivo
properties. Por ejemplo, jdbc.url se refiere a
jdbc:mysql://localhost:3306/proactiv_prueba?autoReconnect=true. En el
archivo configuracon.properties del directorio WEB-INF se encuentra la
siguiente definición:
o
o jdbc.driverClassName=com.mysql.jdbc.Driver
o
jdbc.url=jdbc:mysql://localhost:3306/proactiv_prueba?
autoReconnect=true
o jdbc.username=usuario
o jdbc.password=password
Los controladores
/
**********************************************************************
*************
* Controlador para index.jsp (listado, consulta, inserción, etc)
*********************************************************************
*************/
public class IndexController implements Controller {
PropertyConfigurator.configure( "c:/DOC/Java_Eclipse/spring21_MVC/log/
log4j.properties");
logger.info("spring21_MVC. Iniciado loger de " +
this.getClass().getName());
}
/**************************************************************
*********************
* Conducta básica: devuelve listado de cliente
* Conducta opcional: en función de param de request (consulta,
etc)
**********************************************************************
************/
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response)
String paramOperacion =
request.getParameter("operacion");
if ( paramOperacion != null ) {
//// Si es una consulta ...
if ( paramOperacion.equals("Consultar") ) {
try {
cliente =
servicioCliente.getCliente(request.getParameter("codigo"));
}
catch
(org.springframework.dao.EmptyResultDataAccessException e) {
mensaje = "Cliente no
encontrado";
}
}
//// Si es una inserción ...
if ( paramOperacion.equals("Insertar") ) {
try {
cliente =
servicioCliente.getClienteFromRequest( request );
int resultado =
servicioCliente.insertarCliente( cliente);
if (resultado == 0)
mensaje = "Cliente no
insertado";
}
catch (Exception e) {
mensaje = "Cliente no
insertado";
}
}
//// Si es una actualización ...
if ( paramOperacion.equals("Actualizar") ) {
try {
cliente =
servicioCliente.getClienteFromRequest( request );
int resultado =
servicioCliente.actualizarCliente( cliente);
if (resultado == 0)
mensaje = "Cliente no
actualizado";
}
catch (Exception e) {
mensaje = "Cliente no
actualizado";
}
}
}
mav.addObject( "unCliente", cliente );
mav.addObject( "mensaje", mensaje );
return mav;
}
}
Lo que devuelve cada controlador es un objeto del tipo ModelAndView. Este objeto se
compone de:
En caso de que no haya que hacer ninguna operación sobre cliente, entonces
el cliente que se añade al modelo es null.
index.jsp y JSTL
Lo interesante es observar como recoge la página index.jsp todos estos datos (cliente, lista de
clientes y mensaje) que devuelve IndexController.
....
Para el uso de JSTL necesitamos las librerias jstl.jar y standard.jar. Dentro de la iteración se
realizan llamadas a métodos getXXX() de la clase Cliente para el código, etc. El código
completo de la JSP:
<html xmlns="http://www.w3.org/1999/xhtml">
<CENTER><H4><b>Gestión de clientes</H4></b></CENTER>
</TD></TR></TABLE>
</font></body></html>
• Gracias al uso de controladores y JST no hay una sola línea de Java en la JSP.
• Por la misma razón no ha sido necesario recurrir a Java Beans (estilo usabean).
formulario.jsp
<p>${mensaje}</p>
<br><BR>
<HR>
El action del primer form llama de nuevo al controlador IndexController que devuelve la vista
index.jsp. El action del segundo llama a BorrarController que devuelve la vista index.jsp. El
controlador perfectamente podría devolver otra vista (es lo más habitual), pero en nuestro
ejemplo volvemos de nuevo a index.jsp.
Otros JSPs
cabeceraDePagina.jsp
<head>
<title>Ejemplo de Spring MVC</title>
<meta name="author" content="Ramiro Lago">
<meta name="organization" content="Ramiro Lago">
<meta name="locality" content="Spain">
<meta name="lang" content="es">
<meta name="description" content="JSP">
<meta name="keywords" content="Java, Spring, MVC, JSTL">
<meta http-equiv="Content-Type" content="text/html;
charset=iso-8859-1">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="-1">
</head>
<body bgcolor="#FFFF9D"><FONT color="#000080"
FACE="Arial,Helvetica,Times" SIZE=2>
<CENTER><H3>Ejemplo de Spring MVC</H3></CENTER>
<HR>
error.jsp
<html>
<%@ include file="cabeceraDePagina.jsp" %>
La capa de servicio
package com.servicio;
import com.persistencia.*;
import com.dominio.Cliente;
import java.util.List;
import org.springframework.dao.EmptyResultDataAccessException;
import javax.servlet.http.HttpServletRequest;
/
**********************************************************************
**************
* Clase de servicio. Intermediaria entre vista y DAO
*********************************************************************
****************/
public class ServicioCliente {
private DAOClienteSpring dao;
// @Transactional(readOnly=true)
public List getTodosClientes() {
return dao.selectAll();
}
// @Transactional(readOnly=true)
public Cliente getCliente( String codigo ) throws
EmptyResultDataAccessException {
return dao.selectCliente(codigo);
}
/*****************************************************************
*******************
* Mapea una request sobre un objeto. Devuelve el objeto
*****************************************************************
********************/
public Cliente getClienteFromRequest( HttpServletRequest request )
{