Mostrando las entradas con la etiqueta spring basico. Mostrar todas las entradas
Mostrando las entradas con la etiqueta spring basico. Mostrar todas las entradas

viernes, 21 de agosto de 2009

Spring MVC Usando @Controller y Formularios

En el post anterior vimos como podemos usar el estereotipo @Controller para mapear las solicitudes con la anotación @RequestMapping a métodos, esta vez vamos a ver como podemos usar formularios, validarlos y hacer uso de una clase estereotipada(denuevo @Controller) que controle y mapee adecuadamente las solicitudes que se hagan con el formulario, para esto la configuración de la aplicación no ha cambiado(no he modificado web.xml ni frontController-servlet.xml del post anterior y que voy a reutilizar).

Veamos el primer archivo cliente:
<a href="${pageContext.request.contextPath}/forms/usuario.do">Registrar Usuario</a>

La línea anterior la podemos colocar en cualquier archivo jsp y lo que hará es solicitar al controlador(UsuarioController.java) que me retorne la página donde voy a registrar al usuario, la clase estereotipada UsuarioController.java es la siguiente:


package pe.com.slcsccy.springmvc.controllers;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;
import pe.com.slcsccy.springmvc.dominio.Usuario;
import pe.com.slcsccy.springmvc.validacion.UsuarioValidador;

@Controller
@RequestMapping("/forms/usuario.do")
@SessionAttributes(types = Usuario.class)
public class UsuarioController {

/**
Este método es el que se ejecutará cuando las solicitudes tengan el patrón URL de la forma /forms/usuario.do y sean del tipo GET, es decir desde un link a href, desde un URL, desde javascript o cuando en el formulario html el atributo method sea GET o no esté especificado.
*/
@RequestMapping(method = RequestMethod.GET)
public String inicializarFormulario(Model model) {
/*
Al formulario le enviamos los datos por default de un usuario, este método actua como el inicializador o constructor del formulario
*/
Usuario usuario = new Usuario();
model.addAttribute(usuario);
return "forms/usuario";
}

/*
Este método será ejecutado solo cuando el patrón URL del formulario sea /forms/usuario.do y además el método del formulario que envía los datos solo sea POST, el detalle de los argumentos de los métodos es el siguiente:
@param @ModelAttribute contiene todos los datos del usuario que ha sido enviado desde el formulario
@param BindingResult, BindinResult es una interface, lo que hace Spring es crear un objeto que implementa esta interface,especificamente Spring implementa org.springframework.validation.BeanPropertyBindingResult este objeto se enlazará el objeto usuario enviado para permitir realizar las validaciones necesarias, extiende de Errors.
@param SessionStatus, objeto que determina si el PROCESAMIENTO de la session a sido completado o no, podemos usar este objeto para indicar a la aplicación que el PROCESAMIENTO de la session ha sido completado y que los atributos en él ya no son necesarios.
*/
@RequestMapping(method = RequestMethod.POST)
public String procesarFormulario(@ModelAttribute Usuario usuario,
BindingResult resultado, SessionStatus estado) {
/* Validamos los datos del usuario */
new UsuarioValidador().validar(usuario, resultado);

/* Si no hay errores limpiamos la session y redireccionamos */
if(!resultado.hasErrors()){
/* Limpiamos los objetos de session para este procesamiento*/
estado.setComplete();
/* redireccionamos al método inicializarFormulario */
return "redirect:usuario.do";
}

/* Si hay errores los mostramos en el formulario */
return "forms/usuario";
}

}


La clase anterior usa un validador que es el siguiente:

UsuarioValidador.java
package pe.com.slcsccy.springmvc.validacion;

import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import pe.com.slcsccy.springmvc.dominio.Usuario;

public class UsuarioValidador {

public void validar(Usuario usuario, Errors errors) {

if (!StringUtils.hasLength(usuario.getCodigo())) {
errors.rejectValue("codigo", "required", "required");
}
if (!StringUtils.hasLength(usuario.getNombres())) {
errors.rejectValue("nombres", "required", "required");
}
if (!StringUtils.hasLength(usuario.getApellidos())) {
errors.rejectValue("apellidos", "required", "required");
}
}
}


Espero haber sido lo suficientemente claro en los comentarios java, si tienes dudas no dudes en comentar.

La clase anterior será llamada cuando el usuario hace click en un enlace como:
<a href="${pageContext.request.contextPath}/forms/usuario.do">Registrar Usuario</a>

La clase será llamada porque el url(href) satisface el patrón declarado en la clase:
@RequestMapping("/forms/usuario.do") y el método a ejecutar será inicializarFormulario, porque dicho método está anotado para que le lleguen todas las solicitudes GET @RequestMapping(method = RequestMethod.GET) debes de recordar que una de las formas de realizar solicitudes GET es mediante links(href) en las páginas html.

Una vez ejecutado el método, este instanciarà un objeto usuario y lo devolverá en el modelo de datos a la siguiente vista:

usuario.jsp
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Registro de Usuarios</title>
</head>
<body>
<form:form modelAttribute="usuario">
Codigo: <form:input path="codigo" /><form:errors path="codigo"/><br/>
Nombres: <form:input path="nombres" /><form:errors path="nombres"/><br/>
Apellidos: <form:input path="apellidos" /><form:errors path="apellidos"/><br/><br/><br/>
<input type="submit" value="Registrar Usuario"/>
</form:form>
</body>
</html>


Podemos ver el código html generado y notar que el atributo method(del formulario) es POST, entonces cuando le demos al botón "Registrar Usuario" este procesamiento será enviado hacia el método procesarFormulario de la clase UsuarioController.

Eso es todo por el momento, si tienes alguna duda puedes resolverla comentando...

martes, 18 de agosto de 2009

Spring MVC Usando Controllers con Anotaciones

Veamos como podemos implementar una pequeña aplicación en Spring que use SpringMVC con anotaciones de la manera más fácil posible.

Aquí estan los componentes:

1. Descriptor de despliegue(web.xml):
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<!--
Declaración del servlet que Spring utilizará como Front Controller
para nuestra aplicación.
-->
<servlet>
<servlet-name>frontController</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<!-- Mapping del servlet anterior-->
<servlet-mapping>
<servlet-name>frontController</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>

<!--Tiempo en minutos que dura una session -->
<session-config>
<session-timeout>30</session-timeout>
</session-config>

<!--Listado de archivos de bienvenida -->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>

</web-app>

2. Front Controller de SpringMVC(frontController-servlet.xml)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="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">

<!--
La siguiente declaración buscará todos los estereotipos(Clases declaradas
con las anotaciones @Service,@Component,@Controller,@Repository)
en el paquete pe.com.slcsccy.springmvc.controllers una vez identificado el
estereotipo entonces el contenedor instanciará el bean y por lo tanto estará
listo para ser utilizado por la aplicación
-->
<context:component-scan base-package="pe.com.slcsccy.springmvc.controllers"/>
<!--
El siguiente bean configura las propiedades prefijo(prefix) y sufijo(suffix)
de Internalresourceviewresolver, el cual resuelve nombres de vistas lógicas
retornadas por los controladores(en el ejm:clases en el paquete controllers)
Por ejemplo, un nombre de vista lógico podría ser "resultadoPlanillaQuinta"
entonces esta vista será mapeada a "/WEB-INF/jsp/resultadoPlanillaQuinta.jsp"
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/jsp/" p:suffix=".jsp"/>
</beans>


3. Un Controller de la aplicación(PlanillaController.java) que mapeará las solicitudes a métodos en esta clase:

package pe.com.slcsccy.springmvc.controllers;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class PlanillaController {


/*
Si el método en el controller esta mapeado pero no retorna nada, entonces
Spring lo que hará es buscar una vista con el mismo nombre que el patron
del mapping pero sin la extensión, es decir buscara un archivo jsp con el
siguiente nombre: iniciarApp.jsp, este archivo debe de estar en /WEB-INF/jsp/
*/
@RequestMapping("/iniciarApp.do")
public void iniciarAplicacion() {
System.out.println("Inicio de la aplicación.");
}

/*
El siguiente método acepta todas las solicitudes que vienen del patrón URL(en realidad es una ruta directa) /planilla/procesar.do,este patrón lo declaramos en la anotaciòn @RequestMapping, como salida tenemos un nombre de vista que va estar relacionado a un resolvedor de vistas, quién resolverá el recurso(archivo jsp) a presentar.
*/
@RequestMapping("/planilla/procesarQuinta.do")
public String procesarQuinta(@RequestParam("periodo") String periodo, ModelMap modelo) {
modelo.addAttribute("periodo", periodo);
return "planilla/resultadoPlanillaQuinta";
}

@RequestMapping("/planilla/procesarCuarta.do")
public String procesarCuarta(@RequestParam("periodo") String periodo, ModelMap modelo) {
modelo.addAttribute("periodo", periodo);
return "planilla/resultadoPlanillaCuarta";
}


/*
Este método difiere con los 2 anteriores en el tipo de retorno, ahora el
tipo de retorno no es una cadena sino un ModelMap, este modelMap debe
contener el modelo de datos a ser presentado en la vista, en los métodos
anteriores colocabamos el modelMap como un argumento al método y después
usabamos ese modelMap para colocar atributos(modelo de datos) que iban
a ser pintados en la vista. En este método la vista es resuelta hacia el
mismo URL que lo solicitó, es decir la vista se resuelve hacia:
/WEB-INF/jsp/planilla/cierre.jsp, recordar que el prefijo: WEB-INF/jsp/
y el sufijo son declarados en el frontController:
frontController-servlet.xml, ModelMap si revisas las fuentes de Spring extiende
LinkedHashMap que es una forma especializada de HashMap
*/
@RequestMapping("/planilla/cierre.do")
public ModelMap cerrarPlanilla(@RequestParam("periodo") String periodo) {
/*Aquí consulta compleja a la base de datos para sacar los nombres*/
String[] nombres = {"Carlos","Alberto","Cacique","Yupanqui"};
return new ModelMap("nombres",nombres);
}
}

4. Ahora solo veamos los archivos jsp:
4.1 cierre.jsp (/WEB-INF/jsp/planilla/cierre.jsp)
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Cierre de Planilla</title>
</head>
<body>
<br><br>Planilla Cerrada<br><br>
<table>
<c:forEach var="nombre" items="${nombres}" varStatus="status">
<tr>
<td>${status.index}</td>
<td>${nombres[status.index]}</td>
</tr>
</c:forEach>
</table>
</body>
</html>

4.2 resultadoPlanillaCuarta.jsp (/WEB-INF/jsp/planilla/resultadoPlanillaCuarta.jsp)
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Resultado planilla Cuarta</title>
</head>
<body>
La planilla fue procesada para el periodo ${periodo}
</body>
</html>

4.2 resultadoPlanillaQuinta.jsp (/WEB-INF/jsp/planilla/resultadoPlanillaQuinta.jsp)
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Resultado planilla Quinta</title>
</head>
<body>
La planilla fue procesada para el periodo ${periodo}
</body>
</html>

El desarrollo de esta pequeña aplicación fué realizada con Spring 2.5.6, y el propósito es ver como podemos usar anotaciones en la capa de control de una aplicación MVC. Las anotaciones vistas fueron @Controller y @RequestMapping.

Podemos ver como las solicitudes que enviamos a la aplicació se mapean correctamente probando de la siguiente manera, en el URL de tu navegador puedes poner:


y debería ejecutar el método adecuado sgún el mapping y redirigirse a la vista apropiada.


sábado, 15 de agosto de 2009

Spring Básico, inyección de dependencias desde xml

Este post tratará el concepto de inyección de dependencias declarado en un archivo xml, y ¿cómo? desde código java podemos obtener el ensamblado(todos los beans instanciados y relacionados en memoria), para este caso utilizo un modelo de dominio de pedidos de compra, aquí hay que tener cuidado ya que el objetivo del post es ver como Spring inyecta las dependencias en los objetos que va creando, en un entorno de producción Spring no se utiliza para instanciar objetos de negocio o de un modelo de un dominio especifico(de esto se puede encargar Hibernate, Ibatis o el ORM de tu preferencia), Spring en entornos de producción nos servirá para instanciar servicios, daos, acciones de struts, etc...

Veamos la base de código, del modelo de dominio.

Proveedor.java
package pe.com.slcsccy.springbasico.dominio;
public class Proveedor {
private Integer idProveedor;
private String nombreProveedor;
private String nombreContacto;
private String direccion;
private String telefono;
//siguen setters y getters...
}

PedidoCompra.java
package pe.com.slcsccy.springbasico.dominio;
import java.util.ArrayList;
public class PedidoCompra {
private Integer idPedidoCompra;
private String numeroPedidoCompra;
private String descripcionPedidoCompra;
private String fechaPedido;
private String fechaEntrega;

//Aqui agregamos referencias a un listado de items
//de pedido DetallePedido
private ArrayList detalle;

//Aqui mantenemos la información del proveedor
//a quien se realiza la compra
private Proveedor proveedor;

//siguen setters y getters...
}

DetallePedido.java
package pe.com.slcsccy.springbasico.dominio;
import java.math.BigDecimal;
public class DetallePedido {
private Integer idDetallePedido;
private Integer idPedidoCompra;
private Integer idProducto;
private BigDecimal precioUnitario;
private Integer unidadesPedidas;
//siguen setters y getters...
}

Ahora veamos el archivo de configuración de spring que hace que la magia de la instanciación y la inyección de componentes ó beans se lleve a cabo. El siguiente archivo lo colocamos en el directorio raiz de las clases java

pedido1.xml
<?xml version="1.0" encoding="UTF-8"?>

<!-- Definimos los namespaces que vamos a usar; recordar que un namespace define un enlace(Entre la instancia XML, este archivo, y el esquema con las reglas de formación que debe tener este archivo, el esquema está representado por una URL), el namespace en este archivo lo podemos identificar por la palabra clave xmlns, si despues de esta palabra clave hay dos puntos y otra palabra, pues esta otra palabra es llamada el prefijo que deberemos usar en el archvo XML(este archivo), para este caso el namespace principal que usamos es
beans xmlns="http://www.springframework.org/schema/beans" y como no está asociado a un prefijo pues su uso en ste archivo es a través de la palabra "beans"(ojo podemos usar cualquier otra palabra, es como si declararamos una variable), resumiendo lo dicho entre instancias Xml y esquemas Xsd pues como una analogía podemos tomar un esquema como una clase y un instancia Xml como una instancia(objeto) de esa clase.
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<!--Definimos un bean proveedor-->
<!--Bueno ahora veamos que es lo que Spring hace cuando se encuentra con un bean como este, Spring lo que hará es crear una instancia con el nombre proveedor(atributo id) de la clase Proveedor(atributo class) y le asignará valores a cada una de sus propiedades.
Tambien lo podemos ver de la siguiente manera:
Proveedor proveedor = new Proveedor();
proveedor.setIdProveedor(1000);
proveedor.setNombreProveedor("CcySolutions");
proveedor.setNombreContacto("Carlos Cacique Yupanqui");
proveedor.setDireccion("Av. Tupac Amaru 094");
proveedor.setTelefono("989873846");
-->
<bean id="proveedor" class="pe.com.slcsccy.springbasico.dominio.Proveedor">
<property name="idProveedor" value="1000"/>
<property name="nombreProveedor" value="CcySolutions"/>
<property name="nombreContacto" value="Carlos Cacique Yupanqui"/>
<property name="direccion" value="Av. Tupac Amaru 094"/>
<property name="telefono" value="989873846"/>
</bean>

<!-- Definimos beans del tipo detalle de pedido-->
<!--
Una forma más simplificada de asignar los valores a las propiedades es usando el alias p del namespace http://www.springframework.org/schema/p
-->
<bean id="item1" class="pe.com.slcsccy.springbasico.dominio.DetallePedido"
p:idDetallePedido="1478" p:idPedidoCompra="1000" p:idProducto="1024"
p:precioUnitario="3000.14" p:unidadesPedidas="100"/>

<bean id="item2" class="pe.com.slcsccy.springbasico.dominio.DetallePedido"
p:idDetallePedido="1479" p:idPedidoCompra="1000" p:idProducto="1024"
p:precioUnitario="7800.14" p:unidadesPedidas="10"/>

<!--Tambien podemos signar los valores a las propiedades de la forma más usual-->
<bean id="item3" class="pe.com.slcsccy.springbasico.dominio.DetallePedido">
<property name="idDetallePedido" value="1480"/>
<property name="idPedidoCompra" value="1000"/>
<property name="idProducto" value="1026"/>
<property name="precioUnitario" value="800.14"/>
<property name="unidadesPedidas" value="17"/>
</bean>

<bean id="item4" class="pe.com.slcsccy.springbasico.dominio.DetallePedido"
p:idDetallePedido="1481" p:idPedidoCompra="1000" p:idProducto="1025"
p:precioUnitario="40.14" p:unidadesPedidas="50"/>

<!-- Ahora INYECTAMOS los beans anteriores al bean principal -->
<bean id="pedido1" class="pe.com.slcsccy.springbasico.dominio.PedidoCompra">
<!-- La siguiente linea INYECTA el bean proveedor a este bean(pedido1) -->
<property name="proveedor" ref="proveedor"/>
<property name="idPedidoCompra" value="100000"/>
<property name="numeroPedidoCompra" value="CX-TX-1000"/>
<property name="descripcionPedidoCompra" value="Pedido realizado."/>
<property name="fechaPedido" value="2009-08-15"/>
<property name="fechaEntrega" value="2009-08-17"/>

<!-- Las siguiente líneas INYECTARAN los items anteriores-->
<property name="detalle">
<list>
<ref bean="item1"/>
<ref bean="item2"/>
<ref bean="item3"/>
</list>
</property>
</bean>
</beans>

Ahora veamos la clase principal que levanta el archivo pedido1.xml y muestra los valores de cada instancia/objeto:

Inicio.java
package pe.com.slcsccy.springbasico;
import java.util.ArrayList;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import pe.com.slcsccy.springbasico.dominio.DetallePedido;
import pe.com.slcsccy.springbasico.dominio.PedidoCompra;
import pe.com.slcsccy.springbasico.dominio.Proveedor;

public class Inicio {

public static void main(String[] arg) {
//Instanciamos un contenedor de Spring:
ApplicationContext context = new ClassPathXmlApplicationContext(
new String[]{"pedido1.xml"});

PedidoCompra pedido1 = (PedidoCompra) context.getBean("pedido1");
System.out.println("Datos del Pedido:");
System.out.println("*****Descripción:" + pedido1.getDescripcionPedidoCompra());
System.out.println("*****Fecha Entrega:" + pedido1.getFechaEntrega());

Proveedor proveedor = pedido1.getProveedor();
System.out.println("Datos del Proveedor:");
System.out.println("*****Contacto:" + proveedor.getNombreContacto());
System.out.println("*****Nombre:" + proveedor.getNombreProveedor());

System.out.println("Detalle del pedido:");
ArrayList detalle = pedido1.getDetalle();
for (DetallePedido item : detalle) {
System.out.println("****Precio:"+item.getPrecioUnitario());
}
}
}