Tabla de Contenidos
Spring MVC
Entramos por fin en la parte del desarrollo de una aplicación web. Spring MVC es parte de la funcionalidad de Spring framework pero dedicada a la parte de los Servlets y JSP.
Hemos elegido Spring MVC para la parte web por dos motivos:
- Mejora el uso del API de Servlets pero sin estar muy alejados de ellos. Usar Spring MVC es como usar los Servlets pero con mejoras que nos hacen la vida mas fácil. En Spring en vez de usar Servlets usaremos clases Java llamadas controladores.
- Se integra perectamente con la parte de Inyección de Dependencias de Spring.
No vamos a entrar en detalles sobre cómo funciona Spring MVC sino que vamos a explicar lo mínimo para que se pueda usar Spring MVC.
Ficheros de configuración
Veamos ahora los ficheros que es necesario modificar/crear para poder usar Spring MVC
web.xml
Añadir al fichero web.xml
el siguiente fragmento xml:
- 1 | Fragmento del web.xml
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping>
Este código XML hay que copiarlo tal cual está en el web.xml
pero hay 2 líneas que pueden modificarse para personalizar la aplicación.
- Línea 6: Vemos como se especifica el nombre del fichero de configuración de Spring. En este caso es el mismo que usamos en el tema anterior
classpath:applicationContext.xml
. - Línea 16: Es el patrón que tendrán las URL para que se redireccionen a nuestros controladores de Spring. En nuestro caso , cualquier petición que acabe con “.html” será redirigida hasta los controladores definidos en Spring, pero podemos especificar cualquier patrón para nuestros controladores.
<url-pattern>
el valor de *.jsp
.
Nunca se debe usar ese valor ya que se producirá un error puesto que no sabrá correctamente redireccionar desde los controladores a las páginas JSP de la vista.
dispatcher-servlet.xml
Se deberá crear el fichero dispatcher-servlet.xml
en la carpeta /WEB-INF
. El contenido del mismo será el siguiente:
- 1 | dispatcher-servlet.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.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd "> <mvc:annotation-driven/> <context:component-scan base-package="ejemplo02.presentacion" /> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> </beans>
Este código XML hay que copiarlo tal cual está en el dispatcher-servlet.xml
pero hay 2 líneas que deben modificarse para personalizar la aplicación:
- Línea 13: Se debe indicar el paquete donde se encuentran los controladores web.
- Línea 16: Se indica dónde deben estar las páginas JSP a las que se redirecciona desde los controladores. Se ha elegido
/WEB-INF/jsp/
.Normalmente este valor no es necesario modificarlo ya que las páginas JSP deberían estar dentro de la carpetaWEB-INF
para que desde el navegador no se pueda tener acceso directo a ellas. Debe hacerse a través de los controladores.
<context:component-scan>
para indicar dónde se encuentra el paquete con los controladores web.
context.xml
Este fichero no tiene nada que ver con Spring MVC. El fichero se utiliza para configurar el javax.sql.DataSource que usará el pool de conexiones.
- 1 | context.xml
<?xml version="1.0" encoding="UTF-8"?> <Context antiJARLocking="true" path="/Ejemplo02"> <Resource name="jdbc/hibernate1" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/hibernate1" username="hibernate1" password="hibernate1" maxActive="100" maxIdle="30" maxWait="10000" auth="Container" type="javax.sql.DataSource" validationQuery="SELECT 1 FROM dual" /> </Context>
hibernate.cfg.xml
El fichero hibernate.cfg.xml
es necesario modificarlo para indicarle a Hibernate que obtenga la conexiones a través del DataSource
en vez de crearlas él directamente.
- 1| hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="connection.datasource">java:/comp/env/jdbc/hibernate1</property> <property name="dialect">org.hibernate.dialect.MySQL5Dialect</property> <property name="hibernate.show_sql">true</property> <mapping resource="ejemplo02/persistencia/dao/impl/Profesor.hbm.xml"/> <mapping resource="ejemplo02/persistencia/dao/impl/Usuario.hbm.xml"/> </session-factory> </hibernate-configuration>
Vemos en la línea 6 como se ha incluido la propiedad connection.datasource
y se ha eliminado toda referencia a la base de datos.
applicationContext.xml
No es necesario modificar el fichero applicationContext.xml
de configuración de Spring
SessionFactory e Hibernate
En las aplicaciones de escritorio vimos como se debía llamar a distintos métodos de HibernateUtil
para gestionar el objeto SessionFactory
. En las aplicaciones Web debemos hacer lo mismo para lo que simplemente crearemos la clase Java HibernateContextListenerAndFilter
- 1 | HibernateContextListenerAndFilter.java
@WebListener() @WebFilter(urlPatterns = {"*.html"}) public class HibernateContextListenerAndFilter implements Filter,ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { HibernateUtil.buildSessionFactory(); } @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { try { HibernateUtil.openSessionAndBindToThread(); filterChain.doFilter(servletRequest, servletResponse); } finally { HibernateUtil.closeSessionAndUnbindFromThread(); } } @Override public void destroy() { } @Override public void contextDestroyed(ServletContextEvent sce) { HibernateUtil.closeSessionFactory(); } }
Esta clase se ejecutará al inicio y final de la aplicación y en cada petición web cuya URL tenga el patrón “*.html
”.Para conseguirlo se han añadido anotaciones de Java EE 6 Web @WebListener
y @WebFilter
@WebListener
en Servlet Lifecycle y sobre la anotación @WebFilter
en Filtering Requests and Responses
Lo que sí vamos a comentar son la líneas referidas a HibernateUtil
que tiene ésta clase.
- Línea 7: Al inicio de la aplicación web se inicializa Hibernate con la llamada a
HibernateUtil.buildSessionFactory()
- Línea 33: Al finalizar la aplicación web se cierra Hibernate con la llamada a
HibernateUtil.closeSessionFactory()
. - Línea 18: Al inicio de la petición web se crea la sesión de Hibernate con la llamada a
HibernateUtil.openSessionAndAttachToThread()
- Línea 22: Al finalizar la petición web se destruye la sesión de hibernate con la llamada a
HibernateUtil.closeSessionAndDeattachFromThread()
Como vemos, el patrón utilizado con HibernateUtil
es exactamente igual al usado en las aplicaciones de escritorio con lo que ha sido muy sencillo utilizar HibernateUtil
en una aplicación web.
HibernateContextListenerAndFilter
es necesario usar Tomcat 7 con la versión de Java EE 6 Web.
Si se quisiera usar con con Java EE 5 se podría hacer eliminado las anotaciones y modificando el fichero web.xml
web.xml
En vez de usar la anotaciones:
@WebListener() @WebFilter(urlPatterns = {"*.html"})
Se puede modificar el web.xml añadiendo las siguientes líneas:
<listener> <listener-class>com.fpmislata.persistencia.hibernate.HibernateContextListenerAndFilter</listener-class> </listener> <filter> <filter-name>HibernateContextListenerAndFilter</filter-name> <filter-class>com.fpmislata.persistencia.hibernate.HibernateContextListenerAndFilter</filter-class> </filter> <filter-mapping> <filter-name>HibernateContextListenerAndFilter</filter-name> <url-pattern>*.html</url-pattern> </filter-mapping>
Controllers
Ahora veamos las clases Java que usaremos como controladores en nuestra aplicación web. Estas clases son muy parecidas a los Servlets de Java EE pero más sencillas de usar. Recuerda que usamos estas clases gracias a que estamos usando el framework Spring MVC.
Siguiendo la moda actual, no es necesario que los controladores en Spring MVC extiendan de ninguna otra clase ni que implementen ningún interfaz. Únicamente deben incluir la anotación @Controller
la principio de la clase.
- 1 | ProfesorController.java
@Controller public class ProfesorController { @Autowired private ProfesorDAO profesorDAO; @RequestMapping({"/index.html"}) public ModelAndView read(HttpServletRequest request, HttpServletResponse response) { Map<String, Object> model = new HashMap<>(); String viewName; try { Profesor profesor = profesorDAO.get(1001); model.put("texto", profesor.toString()); viewName = "profesor"; } catch (BussinessException ex) { model.put("msgError", "No es posible obtener los datos"); viewName = "error"; } return new ModelAndView(viewName, model); } }
Pasemos ahora a explicar el código de controlador.
Como ya hemos dicho lo principal que debe tener un controller de Spring MVC es la anotación @Controller
al principio de la clase (Línea 1). Con ésto sabrá Spring MVC que es un controlador.
Manejo de peticiones
El manejo de peticiones web se realiza a través de los métodos del controlador. Cada método gestionará las peticiones de una o más URLs y puede haber tantos métodos como se desee. Ésto es una gran ventaja respecto a un Servlet ya que hace mucho más sencillo procesar una serie de URL relacionadas en un único controlador. Recuerda que en un Servlet había un único método para todas las peticiones.
Cada método que controla peticiones Web tendrá la anotación @RequestMapping
(Línea 7). Esta anotación tendrá como argumento un Array de Strings con todas las URLs que va a manejar. Por ello nótese que hay una llaves “{ }
” antes y después del String de la URL que maneja ya que se permite más de una.
Los argumentos de entrada del método son los objetos HttpServletRequest y HttpServletResponse al igual que en un Servlet.
El método retorna una clase de Spring MVC llamada ModelAndView. Esta clase contendrá un String con la página jsp a mostrar ( es decir la Vista ) y un Map con los datos que necesita la vista para mostrarse (es decir el Modelo).
Podemos ver en la línea 22:
return new ModelAndView(viewName, model);
como se crea un nuevo objeto ModelAndView que tiene como argumentos un String con el nombre de la página JSP a mostrar y el Map con los datos para la vista.
dispatcher-servlet.xml
, la línea 17 , ya se indica que se añadirá como sufijo el texto “.jsp”.
La página JSP
Las páginas JSP se deben colocar en la carpeta WEB-INF/jsp
tal y como se ha definido en el fichero dispatcher-servlet.xml
, en la línea 16.
La vista tiene acceso a los elementos del Map del modelo que se creó en la clase ModelAndView mediante el siguiente código:
Object texto=request.getAttribute("texto");
Siendo el String “texto”
la clave del Map con la que se guardó el valor.
Un ejemplo de página JSP es la siguiente:
- 1 | profesor.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%> <% Object texto=request.getAttribute("texto"); %> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Profesor</title> </head> <body> <h1><%=texto %></h1> </body> </html>
Vemos en la línea 3 cómo se accede a los datos del modelo.