Tutorial Spring 4.2.5
Tutorial Spring 4.2.5
Tabla de Contenidos
Descripcin
1. Contenido
2. Software requerido
3. La aplicacin que vamos a construir
1. Aplicacion Base y Configuracion del Entorno
1.1. Crear la estructura de directorios del proyecto
1.2. Crear 'index.jsp'
1.3. Desplegar la aplicacin en el servidor
1.4. Comprobar que la aplicacin funciona
1.5. Descargar Spring Framework
1.6. Modicar 'web.xml' en el directorio 'src/main/webapp/WEB-INF'
1.7. Crear el Controlador
1.8. Escribir un test para el Controlador
1.9. Crear la Vista
1.10. Compilar, desplegar y probar la aplicacin
1.11. Resumen
2. Desarrollando y Configurando la Vista y el Controlador
2.1. Configurar JSTL y aadir un archivo de cabecera JSP
2.2. Mejorar el controlador
2.3. Separar la vista del controlador
2.4. Resumen
3. Desarrollando la Lgica de Negocio
3.1. Revisar la regla de negocio del Sistema de Mantenimiento de Inventario
3.2. Aadir algunas clases a la lgica de negocio
3.3. Resumen
4. Desarrollando la Interface Web
4.1. Aadir una referencia a la lgica de negocio en el controlador
4.2. Modificar la vista para mostrar datos de negocio y aadir soporte para archivos de mensajes
4.3. Aadir datos de prueba para rellenar algunos objetos de negocio
4.4. Aadir una ubicacin para los mensajes
4.5. Aadir un formulario
4.6. Aadir un controlador de formulario
4.7. Resumen
5. Implementando Persistencia en Base de Datos
5.1. Creacin y rellenado de la base de datos
5.2. Crear una implementacion para JPA de un Objeto de Acceso a Datos (DAO)
5.3. Implementar tests para la implementacion DAO sobre JPA
5.4. Resumen
6. Integrando la Aplicacin Web con la Capa de Persistencia
6.1. Modificar la Capa de Servicio
6.2. Resolver los tests fallidos
6.3. Crear un nuevo contexto de aplicacion para configurar la capa de servicio
6.4. Test final de la aplicacion completa
6.5. Resumen
Descripcin
Este documento es una gua paso a paso sobre cmo desarrollar una aplicacin web,
partiendo de cero, usando Spring Framework.
Se asume que posees un conocimiento superficial de Spring, por lo que este tutorial
te sera til si estas aprendiendo o investigando Spring. Durante el tiempo que
trabajes a travs del material presentado en el tutorial, podrs ver cmo encajan
diversas partes de Spring Framework dentro de una aplicacin web Spring MVC, como
Inversin de Control (Inversion of Control - IoC), Programacin Orientada a Aspectos
(Aspect-Oriented Programming - AOP) y la integracin con las diversas libreras de
servicios (como JPA).
Maven 2
El proyecto SpringSource Tool Suite (http://www.springsource.com/products/sts)
proporciona un excelente entorno para el desarrollo de aplicaciones que utilicen
Spring Framework. SpringSource Tool Suite integra sobre la plataforma Eclipse
(http://www.eclipse.org/), entre otras cosas, el plugin de Spring IDE as como tambin
da soporte al servidor Pivotal tc Server (Tomcat). Por supuesto, puedes usar
cualquier variacin de las versiones de software anteriores. En concreto, los pasos
de este tutorial son reproducibles sobre el servidor GlassFish v3.1.2
(https://glassfish.java.net/). Si quieres usar NetBeans o IntelliJ en lugar de Eclipse, o
Jetty en lugar de Tomcat o GlassFish, ciertos pasos de este tutorial no se
correspondern directamente con tu entorno, pero deberas ser capaz de seguir
adelante de todas maneras.
3. La aplicacin que vamos a construir
La aplicacin que vamos a construir desde cero a lo largo de este tutorial es un
sistema de mantenimiento de inventario muy bsico. Este sistema de mantenimiento
de inventario est muy limitado en alcance y funcionalidad; abajo puedes ver un
diagrama de casos de uso ilustrando los sencillos casos de uso que implementaremos.
La razn por la que la aplicacin es tan limitada es que puedas concentrarte en las
caractersticas especficas de Spring Web MVC y Spring, y olvidar los detalles ms
sutiles del mantenimiento de inventario.
Diagrama de casos de uso de un sistema de mantenimiento de inventario
Una vez hayamos finalizado todos los pasos del tutorial, tendremos una aplicacin
que realiza un mantenimiento bsico de stock, incluyendo listados de stock y el
incremento de precios de los productos de dicho stock.
Captulo 1. Aplicacin Base y Configuracin del Entorno
1.1. Crear la estructura de directorios del proyecto
Necesitamos crear un directorio donde alojar todos los archivos que vayamos
creando, as que comenzaremos creando un proyecto en el Workspace
llamado 'springapp'. Utilizaremos Maven2 para facilitar la creacin de la estructura
de directorios del proyecto as como la inclusin de las libreras necesarias.
A continuacin puedes ver una captura de pantalla que muestra como quedara la
estructura de directorios si has seguido las instrucciones correctamente. (La imagen
muestra dicha estructura desde el SpringSource Tool Suite (STS): no se necesita usar
STS para completar este tutorial, pero usndolo podrs hacerlo de manera mucho
ms sencilla).
Puesto que estamos creando una aplicacin web, Maven ya ha creado un archivo JSP
muy simple llamado 'index.jsp' en el directorio 'src/main/webapp'. El
archivo 'index.jsp' es el punto de entrada a nuestra aplicacin. Para
familiarizarnos con el entorno, podemos cambiarlo por el que se muestra a
continuacin:
'springapp/src/main/webapp/index.jsp':
<html>
<head><title>Example :: Spring Application</title></head>
<body>
<h1>Example - Spring Application</h1>
<p>This is my test.</p>
</body>
</html>
'springapp/src/main/webapp/WEB-INF/web.xml':
<?xml version="1.0" encoding="UTF-8"?>
<display-name>Springapp</display-name>
</web-app>
1.3. Desplegar la aplicacin en el servidor
Para compilar, construir y desplegar la aplicacin automticamente slo es necesario
seleccionar 'Run as > Run on Server' sobre el men contextual que aparece
cuando se pincha el botn derecho sobre el nombre del proyecto. A continuacin,
debemos seleccionar el servidor desde el cuadro de dilogo que ofrece los servidores
dados de alta en el entorno. Por ejemplo: Pivotal tc Server, Tomcat, GlassFish, etc.
1.4. Comprobar que la aplicacin funciona
Los pasos anteriores abrirn una pestaa en el entorno de desarrollo STS donde se
puede ver el contenido de la pgina 'index.jsp'. De manera alternativa, se puede
abrir un navegador y acceder a la pgina de inicio de la aplicacin en la siguiente
URL: http://localhost:8080/springapp.
(La imagen muestra la visualizacin sobre STS: el nmero de puerto puede variar
dependiendo del servidor utilizado).
La pgina de inicio de la aplicacin
joda-time joda-time
'springapp/src/main/webapp/WEB-INF/web.xml':
<?xml version="1.0" encoding="UTF-8"?>
<display-name>Springapp</display-name>
<servlet>
<servlet-name>springapp</servlet-name>
<servlet-
class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/app-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springapp</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
</web-app>
A continuacin, creamos el subdirectorio 'src/main/webapp/WEB-INF/spring' y
dentro el archivo llamado 'app-config.xml'. Este archivo contendr las definiciones
de beans (POJO's) usados por el DispatcherServlet. Es decir, este archivo es
el WebApplicationContext donde situaremos todos los componentes. Por tanto,
utilizaremos el asistente para la creacin de ficheros de tipo 'Spring Bean
Configuration File'. (Si no se utiliza STS y no se dispone del plugin Spring IDE de
Eclipse, basta crear un fichero xml como el que se mostrar a continuacin).
Tras haber introducido el nombre del fichero, es necesario introducir los namespaces
que se utilizarn. Seleccionamos los namespaces 'beans', 'context' y 'mvc' en las
versiones que corresponden con la versin '4.0' de Spring Framework.
Seleccin de los namespaces del fichero 'app-config.xml'
'springapp/src/main/webapp/WEB-INF/spring/app-config.xml':
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
</beans>
1.7. Crear el Controlador
'springapp/src/main/java/com/companyname/springapp/web/HelloController
.java':
package com.companyname.springapp.web;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class HelloController {
@RequestMapping(value="/hello.htm")
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
Los tests son una parte vital del desarrollo del software. El mejor momento para
escribir los tests es durante el desarrollo, no despus, de manera que aunque nuestro
controlador no contiene lgica demasiado compleja vamos a escribir un test para
probarlo. Esto nos permitir hacer cambios en el futuro con total seguridad. Vamos
a crear (si no existiera) un nuevo directorio de tipo 'Source
Folder' llamado 'src/test/java'. Aqu es donde alojaremos todos nuestros tests,
en una estructura de paquetes que ser idntica a la estructura de paquetes que
tenemos en 'src/main/java'.
'springapp/src/test/java/com/companyname/springapp/web/HelloController
Tests.java':
package com.companyname.springapp.web;
import org.junit.Test;
import org.springframework.web.servlet.ModelAndView;
@Test
public void testHandleRequestView() throws Exception{
HelloController controller = new HelloController();
ModelAndView modelAndView = controller.handleRequest(null,
null);
assertEquals("hello.jsp", modelAndView.getViewName());
}
Para ejecutar el test (y todos los tests que escribamos en el futuro), basta con que
seleccionemos 'Run as > JUnit Test' sobre el men contextual que aparece
cuando se pincha el botn derecho sobre la clase de test. Si el test se ejecuta de
forma satisfactoria podrs ver una barra verde que lo indica.
1.9. Crear la Vista
Ahora es el momento de crear nuestra primera vista. Como hemos mencionado antes,
estamos redirigiendo hacia una pgina JSP llamada 'hello.jsp'. Para empezar,
crearemos este fichero en el directorio 'src/main/webapp'.
'springapp/src/main/webapp/hello.jsp':
<html>
<head><title>Hello :: Spring Application</title></head>
<body>
<h1>Hello - Spring Application</h1>
<p>Greetings.</p>
</body>
</html>
1.10. Compilar, desplegar y probar la aplicacin
Para ejecutar la aplicacin de nuevo, seleccionamos de nuevo 'Run as > Run on
Server' sobre el men contextual que aparece cuando se pincha el botn derecho
sobre el nombre proyecto. (En algunos casos, es necasario reiniciar el servidor para
asegurar que la aplicacin se actualiza correctamente).
Probemos esta nueva versin de la aplicacin. Desde el navegador que ofrece STS,
o desde cualquier otro navegador, abrir la URL http://localhost:8080/springapp/hello.htm.
La aplicacin actualizada
1.11. Resumen
Echemos un vistazo rpido a las partes de nuestra aplicacion que hemos creado
hasta ahora.
Group Id
javax.servlet.jsp.jstl jstl-api
org.apache.taglibs taglibs-standard-impl
Vamos a crear un archivo de 'cabecera' que ser embebido en todas las paginas JSP
que escribamos despus. As estaremos seguros de que las mismas definiciones son
incluidas en todos nuestros JSP al insertar el archivo de cabecera. Tambin vamos a
poner todos nuestros archivos JSP en un directorio llamado 'views' bajo el
directorio 'src/main/webapp/WEB-INF'. Esto asegurar que nuestras vistas slo
puedan ser accedidas a travs del controlador, por lo que no ser posible acceder a
estas pginas a travs de una direccin URL. Esta estrategia podra no funcionar en
algunos servidores de aplicaciones; si se es tu caso, mueve el directorio 'views' un
nivel hacia arriba y usa 'src/main/webapp/views' como el directorio de JSP en
todos los ejemplos que encontrars a lo largo del tutorial, en lugar
de 'src/main/webapp/WEB-INF/views'.
Primero creamos un archivo de cabecera para incluir en todos los archivos JSP que
crearemos con posterioridad.
'springapp/src/main/webapp/WEB-INF/views/include.jsp':
<%@ page session="false"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
Ahora podemos actualizar 'index.jsp' para que incluya este archivo, y puesto que
estamos usando JSTL, podemos usar la etiqueta <c:redirect/> para redireccionar
hacia nuestro controlador frontal: Controller. Esto significa que todas nuestras
solicitudes a 'index.jsp' se resolvern a travs de dicho controlador. Elimina los
contenidos actuales de 'index.jsp' y reemplzalos con los siguientes:
'springapp/src/main/webapp/index.jsp':
<%@ include file="/WEB-INF/views/include.jsp" %>
<%-- Redirected because we can't set the welcome page to a virtual URL. --
%>
<c:redirect url="/hello.htm"/>
'springapp/src/main/webapp/WEB-INF/views/hello.jsp':
<%@ include file="/WEB-INF/views/include.jsp" %>
<html>
<head><title>Hello :: Spring Application</title></head>
<body>
<h1>Hello - Spring Application</h1>
<p>Greetings, it is now <c:out value="${now}"/></p>
</body>
</html>
2.2. Mejorar el controlador
Antes de actualizar la localizacin del JSP en nuestro controlador, actualicemos
nuestra unidad de test. Sabemos que necesitamos actualizar la referencia a la vista
con su nueva localizacin, 'WEB-INF/views/hello.jsp'. Tambin sabemos que
debera haber un objeto en el modelo mapeado a la clave "now".
'springapp/src/test/java/com/companyname/springapp/web/HelloController
Tests.java':
package com.companyname.springapp.web;
import org.junit.Test;
import org.springframework.web.servlet.ModelAndView;
@Test
public void testHandleRequestView() throws Exception{
HelloController controller = new HelloController();
ModelAndView modelAndView = controller.handleRequest(null,
null);
assertEquals("WEB-INF/views/hello.jsp",
modelAndView.getViewName());
assertNotNull(modelAndView.getModel());
String nowValue = (String) modelAndView.getModel().get("now");
assertNotNull(nowValue);
'springapp/src/main/java/com/companyname/springapp/web/HelloController
.java':
package com.companyname.springapp.web;
import java.io.IOException;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class HelloController {
@RequestMapping(value="/hello.htm")
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
}
}
Estamos ahora listos para probar nuestras mejoras despus sobre el servidor. Cuando
introduzcamos la direccin http://localhost:8080/springapp/ en un navegador, debera
ejecutarse la pgina de bienvenida 'index.jsp', la cual debera redireccionarnos
a 'hello.htm' que es manejada por el DispatcherServlet, quien a su vez delega
nuestra solicitud al controlador HelloController, que inserta la fecha y hora en el
modelo y las pone a disposicin de la vista 'hello.jsp'.
La aplicacin actualizada
2.3. Separar la vista del controlador
Ahora el controlador especifica la ruta completa a la vista, lo cual crea una
dependencia innecesaria entre el controlador y la vista. Idealmente queremos
referirnos a la vista usando un nombre lgico, permitindonos intercambiar la vista
sin tener que cambiar el controlador. Puedes crear este mapeo en un archivo de
propiedades si estas
usando ResourceBundleViewResolver y SimpleUrlHandlerMapping. Otra opcin
para el mapeo bsico entre una vista y una localizacin, consiste en simplemente
configurar un prefijo y sufijo en InternalResourceViewResolver. Esta solucin es
la que vamos a implantar ahora, por lo que modificamos 'app-config.xml' y
declaramos una entrada 'viewResolver'. Eligiendo JstlView tendremos la
oportunidad de usar JSTL en combinacin con paquetes de mensajes de idioma, los
cuales nos ofreceran soporte para la internacionalizacin de la aplicacin.
'springapp/src/main/webapp/WEB-INF/spring/app-config.xml':
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-
3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView"></property>
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
'springapp/src/test/java/com/companyname/springapp/web/HelloController
Tests.java':
package com.companyname.springapp.web;
import org.junit.Test;
import org.springframework.web.servlet.ModelAndView;
@Test
public void testHandleRequestView() throws Exception{
HelloController controller = new HelloController();
ModelAndView modelAndView = controller.handleRequest(null,
null);
assertEquals("hello", modelAndView.getViewName());
assertNotNull(modelAndView.getModel());
String nowValue = (String) modelAndView.getModel().get("now");
assertNotNull(nowValue);
}
}
'springapp/src/main/java/com/companyname/springapp/web/HelloController
.java':
package com.companyname.springapp.web;
import java.io.IOException;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class HelloController {
@RequestMapping(value="/hello.htm")
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
}
}
A continuacin puedes ver una captura de pantalla que muestra el aspecto que
debera tener la estructura de directorios del proyecto despus de seguir todas las
instrucciones anteriores.
Spring permite hacer las cosas simples fciles y las difciles posibles. La estructura
fundamental que hace esto posible es el uso de Plain Old Java Objects (POJOs u
Objetos Normales Java) por Spring. Los POJOs son esencialmente clases nomales
Java libres de cualquier contrato (normalmente impuesto por un framework o
arquitectura a traves de subclases o de la implementacin de interfaces). Los POJOs
son objetos normales Java que estn libres de dichas obligaciones, haciendo la
programacin orientada a objetos posible de nuevo. Cuando trabajes con Spring, los
objetos de dominio y los servicios que implementes seran POJOs. De hecho, casi todo
lo que implementes debera ser un POJO. Si no es as, deberas preguntarte a ti
mismo porqu no ocurre esto. En esta seccin, comenzaremos a ver la simplicidad y
potencia de Spring.
3.1. Revisando la regla de negocio del Sistema de Mantenimiento
de Inventario
En nuestro sistema de mantenimiento de inventario tenemos dos conceptos: el de
producto, y el de servicio para manejarlo. Supongamos en este tutorial que el negocio
solicita la capacidad de incrementar precios sobre todos los productos. Cualquier
decremento ser hecho sobre cada producto en concreto, pero esta caracterstica
est fuera de la funcionalidad de nuestra aplicacin. Las reglas de validacin para
incrementar precios son:
'springapp/src/main/java/com/companyname/springapp/domain/Product.java
':
package com.companyname.springapp.domain;
import java.io.Serializable;
Escribamos ahora una unidad de test para nuestra clase Product. Algunos
programadores no se molestan en escribir tests para los getters y setters, tambin
llamado cdigo 'auto-generado'. Normalmente, supone demasiado tiempo
enfrascarse en el debate (como este prrafo demuestra) sobre si los getters y setters
necesitan ser testeados, ya que son mtodos demasiado 'triviales'. Nosotros
escribiremos los tests debido a que: a) son triviales de escribir; b) tendremos siempre
los tests y preferimos pagar el precio de perder un poco de tiempo por la sola ocasin
entre cien en que nos salvemos de un error producido por un getter o setter; y c)
porque mejoran la cobertura de los tests. Creamos un stub de Product y testeamos
cada mtodo getter y setter como una pareja en un test simple. Normalmente,
escribirs uno o ms mtodos de test por cada mtodo de la clase, donde cada uno
de estos mtodos compruebe una condicion particular en el mtodo de la clase (como
por ejemplo, verificar un valor null pasado al mtodo, etc.).
'springapp/src/test/java/com/companyname/springapp/domain/ProductTests
.java':
package com.companyname.springapp.domain;
import org.junit.Before;
import org.junit.Test;
public class ProductTests {
@Before
public void setUp() throws Exception {
product = new Product();
}
@Test
public void testSetAndGetDescription() {
String testDescription = "aDescription";
assertNull(product.getDescription());
product.setDescription(testDescription);
assertEquals(testDescription, product.getDescription());
}
@Test
public void testSetAndGetPrice() {
double testPrice = 100.00;
assertEquals(0, 0, 0);
product.setPrice(testPrice);
assertEquals(testPrice, product.getPrice(), 0);
}
'springapp/src/main/java/com/companyname/springapp/service/ProductMana
ger.java':
package com.companyname.springapp.service;
import java.io.Serializable;
import java.util.List;
import com.companyname.springapp.domain.Product;
'springapp/src/main/java/com/companyname/springapp/service/SimpleProdu
ctManager.java':
package com.companyname.springapp.service;
import java.util.List;
import com.companyname.springapp.domain.Product;
Para escribir test efectivos, tienes que considerar todas las pre- y post-condiciones
del mtodo que va a ser testeado, as como lo que ocurre dentro del mtodo.
Comencemos testeando una llamada a getProducts() que devuelve null.
'springapp/src/test/java/com/companyname/springapp/service/SimpleProdu
ctManagerTests.java':
package com.companyname.springapp.service;
import org.junit.Before;
import org.junit.Test;
@Before
public void setUp() throws Exception {
productManager = new SimpleProductManager();
}
@Test
public void testGetProductsWithNoProducts() {
productManager = new SimpleProductManager();
assertNull(productManager.getProducts());
}
}
'springapp/src/test/java/com/companyname/springapp/service/SimpleProdu
ctManagerTests.java':
package com.companyname.springapp.service;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import com.companyname.springapp.domain.Product;
@Before
public void setUp() throws Exception {
productManager = new SimpleProductManager();
products = new ArrayList<Product>();
productManager.setProducts(products);
@Test
public void testGetProductsWithNoProducts() {
productManager = new SimpleProductManager();
assertNull(productManager.getProducts());
}
@Test
public void testGetProducts() {
List<Product> products = productManager.getProducts();
assertNotNull(products);
assertEquals(PRODUCT_COUNT, productManager.getProducts().size());
product = products.get(1);
assertEquals(TABLE_DESCRIPTION, product.getDescription());
assertEquals(TABLE_PRICE, product.getPrice());
}
}
Si volvemos a lanzar los test de SimpleProductManagerTests seguirn fallando.Para
solucionarlo, volvemos a SimpleProductManager e implementamos los mtodos
getter y setter de la propiedad products.
'springapp/src/main/java/com/companyname/springapp/service/SimpleProdu
ctManager.java':
package com.companyname.springapp.service;
import java.util.List;
import com.companyname.springapp.domain.Product;
'springapp/src/test/java/com/companyname/springapp/service/SimpleProdu
ctManagerTests.java':
package com.companyname.springapp.service;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import com.companyname.springapp.domain.Product;
@Before
public void setUp() throws Exception {
productManager = new SimpleProductManager();
products = new ArrayList<Product>();
productManager.setProducts(products);
@Test
public void testGetProductsWithNoProducts() {
productManager = new SimpleProductManager();
assertNull(productManager.getProducts());
}
@Test
public void testGetProducts() {
List<Product> products = productManager.getProducts();
assertNotNull(products);
assertEquals(PRODUCT_COUNT, productManager.getProducts().size());
Product product = products.get(0);
assertEquals(CHAIR_DESCRIPTION, product.getDescription());
assertEquals(CHAIR_PRICE, product.getPrice());
product = products.get(1);
assertEquals(TABLE_DESCRIPTION, product.getDescription());
assertEquals(TABLE_PRICE, product.getPrice());
}
@Test
public void testIncreasePriceWithNullListOfProducts() {
try {
productManager = new SimpleProductManager();
productManager.increasePrice(POSITIVE_PRICE_INCREASE);
}
catch(NullPointerException ex) {
fail("Products list is null.");
}
}
@Test
public void testIncreasePriceWithEmptyListOfProducts() {
try {
productManager = new SimpleProductManager();
productManager.setProducts(new ArrayList<Product>());
productManager.increasePrice(POSITIVE_PRICE_INCREASE);
}
catch(Exception ex) {
fail("Products list is empty.");
}
}
@Test
public void testIncreasePriceWithPositivePercentage() {
productManager.increasePrice(POSITIVE_PRICE_INCREASE);
double expectedChairPriceWithIncrease = 22.55;
double expectedTablePriceWithIncrease = 165.11;
product = products.get(1);
assertEquals(expectedTablePriceWithIncrease, product.getPrice(),
0);
}
}
import java.util.List;
import com.companyname.springapp.domain.Product;
Ahora estamos listos para movernos a la capa web y para poner una lista de
productos en nuestro modelo Controller.
3.3. Resumen
Echemos un rpido vistazo a lo que hemos hecho en la Parte 3.
Hemos escrito tests unitarios para todas las clases que hemos implementado.
No hemos escrito ni una sola linea de cdigo de Spring. ste es un ejemplo de
lo no-intrusivo que es realmente Spring Framework. Uno de sus propsitos
principales es permitir a los programadores centrarse en la parte ms
importante de todas: modelar e implementar requerimientos de negocio. Otro
de sus propsitos es hacer seguir las mejores prcticas de programacin de
una manera sencilla, como por ejemplo implementar servicios usando
interfaces y usar tests unitarios ms all de las obligaciones prgmaticas de
un proyecto dado. A lo largo de este tutorial, vers como los beneficios de
disear interfaces cobran vida.
A continuacin puedes ver una captura de pantalla que muestra el aspecto que
debera tener la estructura de directorios del proyecto despus de seguir todas las
instrucciones anteriores.
La estructura de directorios del proyecto al final de la parte 3
Captulo 4. Desarrollando la Interface Web
sta es la Parte 4 del tutorial paso a paso para desarrollar una aplicacin web desde
cero usando Spring Framework. En la Parte 1 hemos configurado el entorno y montado
la aplicacin bsica. En la Parte 2 hemos mejorado la aplicacin que habamos
construido hasta entonces. La Parte 3 aade toda la lgica de negocio y los tests
unitarios. Ahora es el momento de construir la interface web para la aplicacin.
4.1. Aadir una referencia a la lgica de negocio en el
controlador
Para empezar, renombramos el controlador HelloController a algo ms
descriptivo, como por ejemplo InventoryController, puesto que estamos
construyendo un sistema de inventario. Aqu es donde un IDE con opcin de
refactorizar es de valor incalculable.
Renombramos HelloController a InventoryController as
como HelloControllerTests a InventoryControllerTests. A continuacin,
modificamos InventoryController para que almacene una referencia a la
clase ProductManager. Anotaremos la referencia con @Autowired para que Spring la
pueda inyectar automticamente cuando detecte el componente. Tambin aadimos
cdigo para permitir al controlador pasar la informacin sobre los productos a la vista.
El mtodo getModelAndView() ahora devuelve tanto un Map con la fecha y hora
como una lista de productos.
'springapp/src/main/java/com/companyname/springapp/web/InventoryContro
ller.java':
package com.companyname.springapp.web;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.companyname.springapp.service.ProductManager;
@Controller
public class InventoryController {
@RequestMapping(value="/hello.htm")
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
Antes de que los test puedan ser pasados de nuevo, tambin necesitaremos
modificar InventoryControllerTest para que proporcione un ProductManager y
extraiga el valor de 'now' desde el modelo Map.
'springapp/src/test/java/com/companyname/springapp/web/InventoryContro
llerTests.java':
package com.companyname.springapp.web;
import java.util.Map;
import org.junit.Test;
import org.springframework.web.servlet.ModelAndView;
import com.companyname.springapp.service.SimpleProductManager;
@Test
public void testHandleRequestView() throws Exception{
InventoryController controller = new InventoryController();
controller.setProductManager(new SimpleProductManager());
ModelAndView modelAndView = controller.handleRequest(null,
null);
assertEquals("hello", modelAndView.getViewName());
assertNotNull(modelAndView.getModel());
@SuppressWarnings("unchecked")
Map<String, Object> modelMap = (Map<String, Object>)
modelAndView.getModel().get("model");
String nowValue = (String) modelMap.get("now");
assertNotNull(nowValue);
}
}
4.2. Modificar la vista para mostrar datos de negocio y aadir
soporte para archivos de mensajes
Usando la etiqueta JSTL <c:forEach/>, aadimos una seccin que muestra
informacin de cada producto. Tambin vamos a reemplazar el ttulo, la cabecera y
el texto de bienvenida con una etiqueta JSTL <fmt:message/> que extrae el texto a
mostrar desde una ubicacin 'message' veremos esta ubicacin un poco ms
adelante.
'springapp/src/main/webapp/WEB-INF/views/hello.jsp':
<%@ include file="/WEB-INF/views/include.jsp" %>
<html>
<head><title><fmt:message key="title"/></title></head>
<body>
<h1><fmt:message key="heading"/></h1>
<p><fmt:message key="greeting"/> <c:out value="${model.now}"/></p>
<h3>Products</h3>
<c:forEach items="${model.products}" var="prod">
<c:out value="${prod.description}"/> <i>$<c:out
value="${prod.price}"/></i><br><br>
</c:forEach>
</body>
</html>
4.3. Aadir datos de prueba para rellenar algunos objetos de
negocio
Es el momento de aadir un SimpleProductManager a nuestro archivo de
configuracin, el cual se inyectar automticamente en el InventoryController.
Todava no vamos a aadir ningun cdigo para cargar los objetos de negocio desde
una base de datos. En su lugar, podemos reemplazarlos con unas cuantas instancias
de la clase Product usando beans Spring en el fichero de configuraci de la
aplicacin. Para ello, simplemente pondremos los datos que necesitamos en un
puado de entradas bean en el archivo 'app-config.xml'. Tambin aadiremos el
bean 'messageSource' que nos permitir recuperar mensajes desde la
ubicacin 'messages.properties', que crearemos en el prximo paso.
'springapp/src/main/webapp/WEB-INF/spring/app-config.xml':
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<bean id="productManager"
class="com.companyname.springapp.service.SimpleProductManager">
<property name="products">
<list>
<ref bean="product1"/>
<ref bean="product2"/>
<ref bean="product3"/>
</list>
</property>
</bean>
<bean id="product1"
class="com.companyname.springapp.domain.Product">
<property name="description" value="Lamp"/>
<property name="price" value="5.75"/>
</bean>
<bean id="product2"
class="com.companyname.springapp.domain.Product">
<property name="description" value="Table"/>
<property name="price" value="75.25"/>
</bean>
<bean id="product3"
class="com.companyname.springapp.domain.Product">
<property name="description" value="Chair"/>
<property name="price" value="22.79"/>
</bean>
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages"/>
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView"></property>
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
4.4. Aadir una ubicacin para los mensajes
Creamos un archivo llamado 'messages.properties' en el
directorio 'src/main/webapp/WEB-INF/classes'. Este archivo de propiedades
contiene tres entradas que coinciden con las claves especificadas en las
etiquetas <fmt:message/> que hemos aadido a 'hello.jsp'.
'springapp/src/main/webapp/WEB-INF/classes/messages.properties':
title=SpringApp
heading=Hello :: SpringApp
greeting=Greetings, it is now
La aplicacin actualizada
4.5. Aadir un formulario
Para proveer de una interface a la aplicacin web que muestre la funcionalidad para
incrementar los precios, vamos a aadir un formulario que permitir al usuario
introducir un valor de porcentaje. Para ello, creamos el archivo
JSP 'priceincrease.jsp' en el directorio 'src/main/webapp/WEB-INF/views'.
'springapp/src/main/webapp/WEB-INF/views/priceincrease.jsp':
<%@ include file="/WEB-INF/views/include.jsp" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<html>
<head>
<title><fmt:message key="title"/></title>
<style>
.error { color: red; }
</style>
</head>
<body>
<h1><fmt:message key="priceincrease.heading"/></h1>
<form:form method="post" commandName="priceIncrease">
<table >
<tr>
<td align="right" width="20%">Increase (%):</td>
<td width="20%">
<form:input path="percentage"/>
</td>
<td width="60%">
<form:errors path="percentage" cssClass="error"/>
</td>
</tr>
</table>
<br>
<input type="submit" value="Execute">
</form:form>
<a href="<c:url value="hello.htm"/>">Home</a>
</body>
</html>
Group Id
javax.validation validation-api
org.hibernate hibernate-validator
Group Id
org.slf4j slf4j-api
org.slf4j slf4j-log4j12
'springapp/src/main/resources/log4j.dtd':
<?xml version="1.0" encoding="UTF-8" ?>
<!-- The "threshold" attribute takes a level value such that all -->
<!-- logging statements with a level equal or below this value are -->
<!-- disabled. -->
<!-- Setting the "debug" enable the printing of internal log4j logging -
->
<!-- statements. -
->
<!ATTLIST log4j:configuration
xmlns:log4j CDATA #FIXED "http://jakarta.apache.org/log4j/"
threshold (all|debug|info|warn|error|fatal|off|null) "null"
debug (true|false|null) "null"
>
<!-- renderer elements allow the user to customize the conversion of -->
<!-- message objects to String. -->
<!-- ErrorHandlers can be of any class. They can admit any number of -->
<!-- parameters. -->
<!-- If no level element is specified, then the configurator MUST not -->
<!-- touch the level of the named category. -->
<!ELEMENT category (param*,(priority|level)?,appender-ref*)>
<!ATTLIST category
class CDATA #IMPLIED
name CDATA #REQUIRED
additivity (true|false) "true"
>
<!-- If no level element is specified, then the configurator MUST not -->
<!-- touch the level of the named logger. -->
<!ELEMENT logger (level?,appender-ref*)>
<!ATTLIST logger
name ID #REQUIRED
additivity (true|false) "true"
>
'springapp/src/main/resources/log4j.xml':
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<logger name="org.springframework.jdbc">
<level value="warn" />
</logger>
<logger name="org.springframework.transaction">
<level value="warn" />
</logger>
<logger name="org.springframework.orm">
<level value="warn" />
</logger>
<logger name="org.springframework.web">
<level value="warn" />
</logger>
<logger name="org.springframework.webflow">
<level value="warn" />
</logger>
</log4j:configuration>
'springapp/src/main/java/com/companyname/springapp/service/PriceIncrea
se.java':
package com.companyname.springapp.service;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@Min(0)
@Max(50)
private int percentage;
'springapp/src/main/java/com/companyname/springapp/web/PriceIncreaseFo
rmController.java':
package com.companyname.springapp.web;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.companyname.springapp.service.PriceIncrease;
import com.companyname.springapp.service.ProductManager;
@Controller
@RequestMapping(value="/priceincrease.htm")
public class PriceIncreaseFormController {
@Autowired
private ProductManager productManager;
@RequestMapping(method = RequestMethod.POST)
public String onSubmit(@Valid PriceIncrease priceIncrease,
BindingResult result)
{
if (result.hasErrors()) {
return "priceincrease";
}
productManager.increasePrice(increase);
return "redirect:/hello.htm";
}
@RequestMapping(method = RequestMethod.GET)
protected PriceIncrease formBackingObject(HttpServletRequest request)
throws ServletException {
PriceIncrease priceIncrease = new PriceIncrease();
priceIncrease.setPercentage(15);
return priceIncrease;
}
Para mostrar los distintos mensajes de error, vamos a aadir tambin algunos
mensajes al archivo de mensajes 'messages.properties'.
'springapp/src/main/webapp/WEB-INF/classes/messages.properties':
title=SpringApp
heading=Hello :: SpringApp
greeting=Greetings, it is now
priceincrease.heading=Price Increase :: SpringApp
error.not-specified=Percentage not specified!!!
error.too-low=You have to specify a percentage higher than {0}!
error.too-high=Don''t be greedy - you can''t raise prices by more than {0}%!
required=Entry required.
typeMismatch=Invalid data.
typeMismatch.percentage=That is not a number!!!
<html>
<head><title><fmt:message key="title"/></title></head>
<body>
<h1><fmt:message key="heading"/></h1>
<p><fmt:message key="greeting"/> <c:out value="${model.now}"/></p>
<h3>Products</h3>
<c:forEach items="${model.products}" var="prod">
<c:out value="${prod.description}"/> <i>$<c:out
value="${prod.price}"/></i><br><br>
</c:forEach>
<br>
<a href="<c:url value="priceincrease.htm"/>">Increase Prices</a>
<br>
</body>
</html>
A continuacin puedes ver una captura de pantalla que muestra el aspecto que
debera tener la estructura de directorios del proyecto despus de seguir todas las
instrucciones anteriores.
La estructura de directorios del proyecto al final de la parte 4
Captulo 5. Implementando Persistencia en Base de Datos
Esta es la Parte 5 del tutorial paso a paso sobre sobre cmo desarrollar una aplicacin
web desde cero usando Spring Framework. En la Parte 1 hemos configurado el entorno
y puesto en marcha una aplicacin bsica. En la Parte 2 hemos mejorado la aplicacin
que habamos construido hasta entonces. En la Parte 3 hemos aadido toda la lgica
de negocio y los tests unitarios, y en la Parte 4 hemos desarrollado la interface web.
Ahora es el momento de introducir persistencia en base de datos. En las partes
anteriores hemos visto cmo cargar algunos objetos de negocio definiendo beans en
un archivo de configuracin. Es obvio que esta solucin nunca va a funcionar en el
mundo real cada vez que reiniciemos el servidor obtendremos de nuevo los precios
originales. Necesitamos aadir cdigo para persistir esos cambios en una base de
datos.
5.1. Creacin y rellenado de la base de datos
Antes de que podamos comenzar a desarrollar el cdigo de persistencia, necesitamos
una base de datos. En lugar de confiar en una base de datos integrada con la propia
aplicacin, vamos a utilizar una base de datos separada. Hemos planeado usar MySQL,
una buena base de datos de cdigo libre. Sin embargo, los pasos que se muestran a
continuacin sern similares para otras bases de datos (p. ej. PostgreSQL, HSQL...).
Desde el enlace anterior, se puede descargar MySQL para diferentes sistemas
operativos. Una vez instalada, cada distribucin proporciona sus correspondientes
scripts de inicio para arrancar la base de datos.
'springapp/db/springapp.sql':
CREATE DATABASE springapp;
USE springapp;
Por ltimo, ejecutamos las siguientes instrucciones sobre la lnea de comandos para
crear y rellenar la base de datos:
linux:springapp/db# mysql -u root -p
Enter password:
...
mysql> source springapp.sql
mysql> source load_data.sql
Para poder acceder desde nuestra aplicacin a la base de datos MySQL mediante JPA,
debemos incluir las siguientes dependencias en el fichero 'pom.xml':
Group Id Artifac
mysql mysql-connector-java
org.hibernate.javax.persistence hibernate-jpa-2.0-api
org.hibernate hibernate-entitymanager
org.springframework spring-orm
'springapp/src/main/java/com/companyname/springapp/repository/ProductD
ao.java':
package com.companyname.springapp.repository;
import java.util.List;
import com.companyname.springapp.domain.Product;
'springapp/src/main/java/com/companyname/springapp/repository/JPAProdu
ctDao.java':
package com.companyname.springapp.repository;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import com.companyname.springapp.domain.Product;
@Repository(value = "productDao")
public class JPAProductDao implements ProductDao {
/*
* Sets the entity manager.
*/
@PersistenceContext
public void setEntityManager(EntityManager em) {
this.em = em;
}
@Transactional(readOnly = true)
@SuppressWarnings("unchecked")
public List<Product> getProductList() {
return em.createQuery("select p from Product p order by
p.id").getResultList();
}
@Transactional(readOnly = false)
public void saveProduct(Product prod) {
em.merge(prod);
}
Vamos a echarle un vistazo a los dos mtodos DAO en esta clase. El primer
mtodo, getProductList(), ejecuta una consulta usando el EntityManager. Para
ello incluimos en l una sentencia SQL que obtiene los objetos persistentes de la
clase Product. El segundo mtodo, saveProduct(), tambin usa el EntityManager.
Esta vez hacemos un merge para almacenar el producto en la base de datos. Ambos
mtodos se ejecutan de manera transaccional gracias a la
anotacin @Transactional, con la diferencia de que el
mtodo getProductList() permite la ejecucin de diversas consultas de lectura en
paralelo.
Llegados a este punto, debemos modificar la clase Product para que se persista
correctamente. Para ello modificamos el fichero 'Product.java' y aadimos las
anotaciones de JPA que realizan el mapeo entre los campos del objeto y aquellos de
la base de datos. Asimismo, hemos aadido el campo id para mapear la clave
primaria de la tabla products.
'springapp/src/main/java/com/companyname/springapp/domain/Product.java
':
package com.companyname.springapp.domain;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="products")
public class Product implements Serializable {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String description;
private Double price;
'springapp/src/test/java/com/companyname/springapp/repository/JPAProdu
ctDaoTests.java':
package com.companyname.springapp.repository;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.companyname.springapp.domain.Product;
@Before
public void setUp() throws Exception {
context = new ClassPathXmlApplicationContext("classpath:test-
context.xml");
productDao = (ProductDao) context.getBean("productDao");
}
@Test
public void testGetProductList() {
List<Product> products = productDao.getProductList();
assertEquals(products.size(), 3, 0);
}
@Test
public void testSaveProduct() {
List<Product> products = productDao.getProductList();
Product p = products.get(0);
Double price = p.getPrice();
p.setPrice(200.12);
productDao.saveProduct(p);
p2.setPrice(price);
productDao.saveProduct(p2);
}
}
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<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>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFacto
ryBean"
p:dataSource-ref="dataSource"
p:jpaVendorAdapter-ref="jpaAdapter">
<property name="loadTimeWeaver">
<bean
class="org.springframework.instrument.classloading.InstrumentationLoadTime
Weaver"/>
</property>
<property name="persistenceUnitName"
value="springappPU"></property>
</bean>
<bean id="jpaAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter
"
p:database="${jpa.database}"
p:showSql="${jpa.showSql}"/>
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory"/>
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- Scans the classpath of this application for @Components to deploy
as beans -->
<context:component-scan base-
package="com.companyname.springapp.repository" />
<context:component-scan base-
package="com.companyname.springapp.service" />
</beans>
'springapp/src/test/resources/jdbc.properties' y 'springapp/src/main/we
bapp/WEB-INF/classes/jdbc.properties':
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/springapp
jdbc.username=springappuser
jdbc.password=pspringappuser
hibernate.dialect=org.hibernate.dialect.MySQLDialect
jpa.database = MYSQL
hibernate.generate_statistics = true
hibernate.show_sql = true
jpa.showSql = true
jpa.generateDdl = true
'springapp/src/main/resources/META-INF/persistence.xml':
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="springappPU" transaction-
type="RESOURCE_LOCAL">
</persistence-unit>
</persistence>
A continuacin puedes ver una captura de pantalla que muestra el aspecto que
debera tener la estructura de directorios del proyecto despus de seguir todas las
instrucciones anteriores.
'springapp/src/main/java/com/companyname/springapp/service/SimpleProdu
ctManager.java':
package com.companyname.springapp.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.companyname.springapp.domain.Product;
import com.companyname.springapp.repository.ProductDao;
@Component
public class SimpleProductManager implements ProductManager {
@Autowired
private ProductDao productDao;
'springapp/src/test/java/com/companyname/springapp/repository/InMemory
ProductDao.java':
package com.companyname.springapp.repository;
import java.util.List;
import com.companyname.springapp.domain.Product;
'springapp/src/test/java/com/companyname/springapp/service/SimpleProdu
ctManagerTests.java':
package com.companyname.springapp.service;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import com.companyname.springapp.domain.Product;
import com.companyname.springapp.repository.InMemoryProductDao;
import com.companyname.springapp.repository.ProductDao;
@Before
public void setUp() throws Exception {
productManager = new SimpleProductManager();
products = new ArrayList<Product>();
@Test
public void testGetProductsWithNoProducts() {
productManager = new SimpleProductManager();
productManager.setProductDao(new InMemoryProductDao(null));
assertNull(productManager.getProducts());
}
@Test
public void testGetProducts() {
List<Product> products = productManager.getProducts();
assertNotNull(products);
assertEquals(PRODUCT_COUNT, productManager.getProducts().size());
product = products.get(1);
assertEquals(TABLE_DESCRIPTION, product.getDescription());
assertEquals(TABLE_PRICE, product.getPrice());
}
@Test
public void testIncreasePriceWithNullListOfProducts() {
try {
productManager = new SimpleProductManager();
productManager.setProductDao(new InMemoryProductDao(null));
productManager.increasePrice(POSITIVE_PRICE_INCREASE);
}
catch(NullPointerException ex) {
fail("Products list is null.");
}
}
@Test
public void testIncreasePriceWithEmptyListOfProducts() {
try {
productManager = new SimpleProductManager();
productManager.setProductDao(new InMemoryProductDao(new
ArrayList<Product>()));
//productManager.setProducts(new ArrayList<Product>());
productManager.increasePrice(POSITIVE_PRICE_INCREASE);
}
catch(Exception ex) {
fail("Products list is empty.");
}
}
@Test
public void testIncreasePriceWithPositivePercentage() {
productManager.increasePrice(POSITIVE_PRICE_INCREASE);
double expectedChairPriceWithIncrease = 22.55;
double expectedTablePriceWithIncrease = 165.11;
product = products.get(1);
assertEquals(expectedTablePriceWithIncrease, product.getPrice(),
0);
}
}
'springapp/src/test/java/com/companyname/springapp/web/InventoryContro
llerTests.java':
package com.companyname.springapp.web;
import java.util.ArrayList;
import java.util.Map;
import org.junit.Test;
import org.springframework.web.servlet.ModelAndView;
import com.companyname.springapp.domain.Product;
import com.companyname.springapp.repository.InMemoryProductDao;
import com.companyname.springapp.service.SimpleProductManager;
@Test
public void testHandleRequestView() throws Exception{
InventoryController controller = new InventoryController();
SimpleProductManager spm = new SimpleProductManager();
spm.setProductDao(new InMemoryProductDao(new
ArrayList<Product>()));
controller.setProductManager(spm);
//controller.setProductManager(new SimpleProductManager());
ModelAndView modelAndView = controller.handleRequest(null,
null);
assertEquals("hello", modelAndView.getViewName());
assertNotNull(modelAndView.getModel());
@SuppressWarnings("unchecked")
Map modelMap = (Map) modelAndView.getModel().get("model");
String nowValue = (String) modelMap.get("now");
assertNotNull(nowValue);
}
}
'springapp/src/main/webapp/WEB-INF/spring/app-config.xml':
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages"/>
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView"></property>
<property name="prefix" value="/WEB-INF/views/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
</beans>
'springapp/src/main/webapp/WEB-INF/web.xml':
<?xml version="1.0" encoding="UTF-8"?>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-
class>org.springframework.web.context.ContextLoaderListener</listener-
class>
</listener>
<display-name>Springapp</display-name>
<servlet>
<servlet-name>springapp</servlet-name>
<servlet-
class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/app-config.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springapp</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
</web-app>
Ahora creamos un nuevo archivo 'applicationContext.xml' en el
directorio '/WEB-INF/spring'".
'springapp/src/main/webapp/WEB-INF/spring/applicationContext.xml':
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<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>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFacto
ryBean"
p:dataSource-ref="dataSource"
p:jpaVendorAdapter-ref="jpaAdapter">
<property name="loadTimeWeaver">
<bean
class="org.springframework.instrument.classloading.InstrumentationLoadTime
Weaver"/>
</property>
<property name="persistenceUnitName"
value="springappPU"></property>
</bean>
<bean id="jpaAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter
"
p:database="${jpa.database}"
p:showSql="${jpa.showSql}"/>
<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory"/>
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
6.4. Test final de la aplicacin completa
Ahora es el momento de ver si todas estas piezas funcionan juntas. Construye y
despliega la aplicacin finalizada y recuerda tener la base de datos arrancada y
funcionando. Esto es lo que deberas ver cuando apuntes tu navegador web a la
aplicacin:
La aplicacin completa
La aplicacin aparece exactamente como lo haca antes. Sin embargo, hemos aadido
la persistencia en base de datos, por lo que si cierras la aplicacin tus incrementos
de precio no se perdern sino que estarn todava all cuando vuelvas a cargar la
aplicacin.
6.5. Resumen
Hemos completado las tres capas de la aplicacin -- la capa web, la capa de servicio
y la capa de persistencia. En esta ltima parte hemos reconfigurado la aplicacin.
Despus hemos tenido que arreglar algunos fallos en los tests de la capa de
servicio y la capa web.
A continuacin puedes ver una captura de pantalla que muestra el aspecto que
debera tener la estructura de directorios del proyecto despus de seguir todas las
instrucciones anteriores.