Spring MVC


Introducción

¿Cuántas veces no has soñado con hacerte la vida más fácil? Es grato imaginar que puedes tener una varita mágica que te facilite tus actividades cotidianas. En la programación, por ejemplo, que hubiera una manera de escribir menos código, pero que lograra el objetivo para el cual fue creado. Lo anterior es posible, en efecto, estás leyendo bien, es posible facilitarnos la vida por medio de los frameworks que son entornos de trabajo que nos facilitan el trabajo.

Para Java, uno de los frameworks con más popularidad durante los últimos años es Spring, que nos permitirá poder crear un programa que requiere de bastante complejidad, pero de una forma más fácil.


Explicación

Introducción a MVC

Mozilla (2021) define MVC (modelo-vista-controlador) como un patrón en el diseño de software comúnmente utilizado para implementar interfaces de usuario, datos y lógica de control. Este patrón de diseño hace énfasis en la lógica de negocio y la visualización.

El patrón de diseño MVC consta de tres partes fundamentales:

Figura 1. Patrón de diseño MVC.

Puedes aplicar este patrón de diseño en cualquier lenguaje de programación, pero en especial en Java. Generalmente lo puedes aplicar con Spring. Es un framework bastante completo de Java que te facilita el desarrollo de aplicaciones basadas en Java, Kotlin y Goovy. Spring ofrece seguridad y fácil integración a datos desde Web.

El marco de trabajo modelo-vista-controlador (MVC) de Spring Web está diseñado en torno a un DispatcherServlet, que es un servlet que procesa peticiones y las distribuye para que sean visualizados y facilite el desarrollo de aplicaciones web. Spring Web MVC está orientado a las peticiones y se integra con el framework de Spring, lo que lo hace una solución completa. El manejador por defecto “se basa en las anotaciones @Controller y @RequestMapping, ofreciendo una amplia gama de métodos de manejo flexibles” (Spring, s.f.).

A partir de Spring 3.0 se realizaron integraciones para permitir crear sitios y aplicaciones web fácilmente, mediante el uso de anotaciones y la inyección de dependencias.

Inyección de dependencias

Seguramente, alguna vez en tu vida te has encontrado a una persona que completa las frases antes de que tú las termines o que esté pensando lo mismo que tú en ese momento. Pues algo parecido sucede con la inyección de dependencias en Spring. Es como si el framework hiciera lo que nosotros estamos pensando, es decir, Spring inyecta los objetos que tenemos que instanciar sin que nosotros lo hagamos cuando los necesite. Es decir, nunca pondremos la palabra reservada new para instanciar un objeto.

La inyección de dependencias se debe configurar en un fichero XML por medio de etiquetas llamadas beans. El siguiente ejemplo muestra la forma correcta de escribir la etiqueta:

<bean id="persona" class="com.spring.example.Persona">
    <property name="nombre" value="Juan" />
    <property name="apellido" value="Perez" />
</bean>

Tabla 1. Ejemplo de inyección de dependencias en Spring.

Lo anterior podría ser posible teniendo la clase Persona definida así:

package com.spring.example.Persona;

 

public class Persona {

  private String nombre;

  private String apellido;

}

Tabla 2. Ejemplo de declaración de clase.

Así, por ejemplo, si deseamos obtener los datos de Juan, lo haríamos de la siguiente forma:

package com.spring.example.Persona;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

  public class Main {

     public static void main(String[] args) {
       BeanFactory factory = new  
       ClassPathXmlApplicationContext("META-INF/spring/app-context.xml");
       Persona persona = (Persona) factory.getBean("persona");
       System.out.println("- " + persona.getNombre() + " " + persona.getApellido());
      }

}

Tabla 3. Ejemplo de clase principal con inyección de dependencias.

Como te habrás dado cuenta, tú puedes ocupar el patrón de inyección de dependencias para que los componentes declaren sus dependencias. Sin embargo, no se encargarán de obtenerlas. Para esto, se ocupa un contenedor de Spring que se encargará de almacenar objetos de servicio, DAO’s que son objetos de acceso a datos y otros objetos para conectarte a una base de datos.

DAO’s (Data Access Objects)
Los DAO’s son un patrón de acceso a datos que se utiliza para manipular los datos de bajo nivel. Se representa a través de la siguiente estructura:

Figura 2. Patrón de acceso a datos DAO.

Para aplicar el patrón DAO lo haremos con un ejemplo en el que tendrás una base de datos de empleados y cada empleado tendrá un nombre y un identificador.

Primero debes definir la clase modelo que contendrá los datos del empleado.

package dao;

 

public class Empleado {

  private String name;

  private int id;

 

  Empleado(String name, int id){

   this.name = name;

   this.id = id;

  }

 

  public String getName() {

   return name;

  }

  public void setName(String name) {

   this.name = name;

  }

 

  public int getId() {

   return id;

  }

 

  public void setId(int id) {

   this.id = id;

  }

}

Tabla 4. Clase modelo de acceso a datos.

Luego debes definir la interfaz de objetos.

package dao;

import java.util.List;

 

public interface EmpleadoDAO {

  public Empleado getEmpleado(int id);

  public void updateEmpleado(Empleado empleado);

  public void deleteEmpleado(Empleado empleado);

  public List<Empleado> getAllEmpleados();

}

Tabla 5. Interfaz de objetos de acceso a datos.

Posteriormente, tendrás que crear clase concreta que es la implementación de la interfaz de objetos donde se realizará el desarrollo de las acciones declaradas en la interfaz de objetos.

package dao;

import java.util.ArrayList;

import java.util.List;

 

public class EmpleadoDAOImp implements EmpleadoDAO{

  List<Empleado> empleados;

 

  public EmpleadoDAOImp(){

   empleados = new ArrayList<Empleado>();

   Empleado student1 = new Empleado("Manuel",0);

   Empleado student2 = new Empleado("Eduardo",1);

   empleados.add(student1);

   empleados.add(student2);  

  }

 

  @Override

  public Empleado getEmpleado(int id){

    return empleados.get(id);

  }

 

  @Override

  public void updateEmpleado(Empleado empleado){

    empleados.get(empleado.getId()).setName(empleado.getName());

    System.out.println("** El Empleado con Id " + empleado.getId() + " se actualizó.");

  }

 

  @Override

  public void deleteEmpleado(Empleado empleado){

    empleados.remove(empleado.getId());

    System.out.println("** Empleado con Id " + empleado.getId() + " se eliminó.");

  }

  @Override

  public List<Empleado> getAllEmpleados(){

    return empleados;

  }

 

}

Tabla 6. Clase concreta de acceso a datos.

Para ocupar lo anterior deberás crear una clase adicional con el método “main”.

package dao;

 

public class DemoDAO {

  public static void main(String[] args) {

    EmpleadoDAO empleadoDao = new EmpleadoDAOImp();

 

    for (Empleado empleado : empleadoDao.getAllEmpleados()) {

      System.out.println("Empleado: { Id : " + empleado.getId() + ", Name : " + empleado.getName() + " }");

    }

 

    Empleado empleado =empleadoDao.getAllEmpleados().get(0);

    empleado.setName("Adela");

    empleadoDao.updateEmpleado(empleado);

 

    empleadoDao.getEmpleado(0);

    System.out.println("Empleado: { Id : " + empleado.getId() + ", Name : " + empleado.getName() + " }");    

   }

}

Tabla 7. Clase de demostración con método main.

Clases y anotaciones de Spring

Las anotaciones son una forma de metadatos y “proporcionan datos sobre un programa que no forman parte del propio programa” (Oracle, s.f.-a). Las anotaciones no tienen ningún efecto directo sobre el funcionamiento del código.

Oracle, (s.f.) menciona los siguientes usos para las anotaciones:

Figura 3. Usos para las anotaciones.

Para indicar una anotación se realiza por medio de @ seguido del nombre de la anotación. En el siguiente ejemplo, el nombre de la anotación es Override:

public class Anotacion {

  @Override

  public String toString() {

    return "example";

  }

}

Tabla 8. Ejemplo de anotación en Java.

Una anotación puede contener elementos que pueden tener nombre o no, y se le pueden asignar valores a esos elementos.

@Author(

  name = "José Saramago",

  date = "02/07/2018"

)

class MyClass { ... }

Tabla 9. Ejemplo de anotaciones con elementos.

Oracle (s.f.-a) menciona que existe un conjunto de tipos de anotaciones están predefinidos en la API de Java SE. Algunos tipos de anotación son utilizados por el compilador de Java, y otros se aplican a otras anotaciones. Los tipos de anotación predefinidos en java.lang son @Deprecated, @Override y @SuppressWarnings.

Figura 4. Principales anotaciones en Java.

Uso del patrón Data Transfer Object (DTO)

Los objetos de transferencia de datos, de acuerdo con Oracle (s.f.-b), sirven para encapsular los datos de negocio. Se utiliza una única llamada a un método para enviar y recuperar el DTO. Cuando el cliente solicita al servidor los datos de negocio, el servidor puede construir el objeto de transferencia, rellenarlo con sus valores de atributos y pasarlo por valor al cliente.

Los objetos de transferencia de datos, a diferencia de los verdaderos objetos orientados a objetos, solo contienen elementos de datos. No proporcionan ningún método de comportamiento. Los DTO deben ser de solo lectura y serializables. El que sea serializable ayuda a que Java pueda convertir fácilmente una clase en un conjunto de bytes y así pueda viajar por la red, y lo hace a través de la interfaz serializable que está disponible en la clase del paquete Java.io.

package dtoExample;

 

import java.io.Serializable;

 

public class personaDTO implements Serializable{

 

  private int id;

  private String nombre;

  private String apellido;

  private String direccion;

}

Tabla 10. Ejemplo de clase DTO.

Implementación DTO con ModelMapper

Los DTOs son patrones que se ocupan como contenedores de datos con poca lógica, por lo que en algunas ocasiones requieren de la clase ModelMapper que permite copiar estos datos de objetos a una nueva instancia en una clase diferente.

Para poder entender la combinación de clases, plantearemos un ejemplo en el que se tiene que entregar una orden de comida a un domicilio, para lo que necesitamos tres clases: Orden, Cliente y Direccion. La clase Orden tendrá atributos de las clases Cliente y Direccion.

package dtoexample.modelmappers;

 

public class Cliente {

  private int id;

  private String Nombre;

 

  public Cliente(int id, String Nombre) {

    this.id = id;

    this.Nombre = Nombre;

  }

 

  public int getId() {

    return this.id;

  }

 

  public String getNombre() {

    return this.Nombre;

  }

 

}

Tabla 11. Ejemplo de clase Cliente.

package dtoexample.modelmappers;

 

public class Direccion {

  private String callenumero;

  private String colonia;

  private String ciudad;

 

 

  public Direccion(String callenumero, String colonia, String ciudad) {

    this.callenumero = callenumero;

    this.colonia = colonia;

    this.ciudad = ciudad;

  }

 

  public String getCallenumero() {

    return this.callenumero;

  }

 

  public String getColonia() {

    return this.colonia;

  }

 

  public String getCiudad() {

    return this.ciudad;

  }

 

}

Tabla 12. Ejemplo de clase Direccion.

package dtoexample.modelmappers;

 

public class Orden {

  private Cliente cliente;

  private Direccion direccion;

 

  public Orden(Cliente cliente, Direccion direccion){

    this.cliente = cliente;

    this.direccion = direccion;

  }

  public Cliente getCliente(){

    return cliente;

  }

  public Direccion getDireccion(){

    return direccion;

  }

}

Tabla 13. Ejemplo de clase Orden.

A continuación, construiremos la clase DTO.

package dtoexample.modelmappers;

 

public class OrdenDTO {

 private int clienteId;

 private String clienteNombre;

 private String direccionCallenumero;

 private String direccionColonia;

 private String direccionCiudad;

 

 public int getClienteId() {

  return this.clienteId;

 }

 

 public void setClienteId(int clienteId) {

  this.clienteId = clienteId;

 }

 

 public String getClienteNombre() {

  return this.clienteNombre;

 }

 

 public void setClienteNombre(String clienteNombre) {

  this.clienteNombre = clienteNombre;

 }

 

 public String getDireccionCallenumero() {

  return this.direccionCallenumero;

 }

 

 public void setDireccionCallenumero(String direccionCallenumero) {

  this.direccionCallenumero = direccionCallenumero;

 }

 

 public String getDireccionColonia() {

  return this.direccionColonia;

 }

 public void setDireccionColonia(String direccionColonia) {

  this.direccionColonia = direccionColonia;

 }

 

 public String getDireccionCiudad() {

  return this.direccionCiudad;

 }

 

 public void setDireccionCiudad(String direccionCiudad) {

  this.direccionCiudad = direccionCiudad;

 }

}

Tabla 14. Ejemplo de clase OrdenDTO.

Aplicamos las clases anteriores a una instancia de la clase ModelMapper ocupando Spring Boot.

package dtoexample.modelmappers;

 

import io.github.picodotdev.blogbitix.modelmapper.classes.Address;

import io.github.picodotdev.blogbitix.modelmapper.classes.Customer;

import io.github.picodotdev.blogbitix.modelmapper.classes.Order;

import io.github.picodotdev.blogbitix.modelmapper.classes.OrderDTO;

import org.modelmapper.ModelMapper;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.CommandLineRunner;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.context.annotation.Bean;

 

@SpringBootApplication

public class Main implements CommandLineRunner {

  @Autowired

  private ModelMapper modelMapper;

 

  @Bean

  ModelMapper modelMapper() {

    return new ModelMapper();

  }

 

  @Override

  public void run(String... args) throws Exception {

    Cliente cliente = new Cliente(1, "Octavio Cruz");

    Direccion direccion = new Direccion("Principal 12", "Conocida", "México");

    Orden orden = new Orden(cliente, direccion);

 

    OrdenDTO ordenDTO = modelMapper.map(order, OrdenDTO.class);

 

    System.out.printf("Cliente - Id: %d%n", ordenDTO.getClienteId());

    System.out.printf("Cliente - Nombre: %s%n", ordenDTO.getClienteNombre());

    System.out.printf("Direccion - Calle y número: %s%n", ordenDTO.getDireccionCallenumero());

    System.out.printf("Direccion - Colonia: %s%n", ordenDTO.getDireccionColonia());

    System.out.printf("Direccion - Ciudad: %s%n", ordenDTO.getDireccionCiudad());

  }

 

  public static void main(String[] args) {

    SpringApplication.run(Main.class, args);

  }

}

Tabla 15. Ejemplo de implementación de la clase ModelMapper.

 


Cierre

Como hemos visto, el patrón de inyección de dependencias nos permite crear aplicaciones menos acopladas, lo cual facilitará la tarea de crear Test Unitarios. Además, nos libra de mucho código para unir las distintas dependencias con las clases que las necesitan, lo que nos ayudará para un código más limpio y reusable.

Figura 5. Conceptos importantes en Java.


Checkpoint

Asegúrate de:

  • Enlistar las ventajas que tiene la arquitectura MVC para comprender mejor la lógica de negocio en una aplicación.
  • Aplicar las anotaciones en las clases que construyas para evitar errores de compilación durante la construcción de tu aplicación.
  • Comprender para qué nos sirven los DTOs para implementarlos en una situación de la vida cotidiana.

Bibliografía

  • Mozilla. (2021). MVC. Recuperado de https://developer.mozilla.org/es/docs/Glossary/MVC
  • Oracle. (s.f.-a). The Java Tutorials. Lesson: Annotations. Recuperado de https://docs.oracle.com/javase/tutorial/java/annotations/
  • Oracle. (s.f.-b). Core J2EE Patterns - Transfer Object. Recuperado de https://www.oracle.com/java/technologies/transfer-object.html
  • Spring. (s.f.). Web MVC framework. Recuperado de https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/mvc.html

La obra presentada es propiedad de ENSEÑANZA E INVESTIGACIÓN SUPERIOR A.C. (UNIVERSIDAD TECMILENIO), protegida por la Ley Federal de Derecho de Autor; la alteración o deformación de una obra, así como su reproducción, exhibición o ejecución pública sin el consentimiento de su autor y titular de los derechos correspondientes es constitutivo de un delito tipificado en la Ley Federal de Derechos de Autor, así como en las Leyes Internacionales de Derecho de Autor.

El uso de imágenes, fragmentos de videos, fragmentos de eventos culturales, programas y demás material que sea objeto de protección de los derechos de autor, es exclusivamente para fines educativos e informativos, y cualquier uso distinto como el lucro, reproducción, edición o modificación, será perseguido y sancionado por UNIVERSIDAD TECMILENIO.

Queda prohibido copiar, reproducir, distribuir, publicar, transmitir, difundir, o en cualquier modo explotar cualquier parte de esta obra sin la autorización previa por escrito de UNIVERSIDAD TECMILENIO. Sin embargo, usted podrá bajar material a su computadora personal para uso exclusivamente personal o educacional y no comercial limitado a una copia por página. No se podrá remover o alterar de la copia ninguna leyenda de Derechos de Autor o la que manifieste la autoría del material.