Compartir en Twitter
Go to Homepage

TUTORIAL DE SPRING BOOT PARA APLICACIONES MODERNAS

October 18, 2025

Introducción a Spring Boot para aplicaciones modernas

Spring Boot es un marco de trabajo robusto y versátil que simplifica el desarrollo de aplicaciones Java, especialmente para proyectos web y empresariales. Este tutorial te guiará paso a paso en la creación de un sistema de reservas de amenidades, ideal para un entorno como un complejo de apartamentos, donde los usuarios pueden reservar servicios como gimnasio, piscina o sauna, respetando límites de capacidad. Usaremos herramientas modernas como Spring Boot, Hibernate, Thymeleaf y Spring Security para construir una aplicación funcional y segura en poco tiempo. Este enfoque es perfecto para prototipos rápidos, como los desarrollados en hackathons o startups, donde la prioridad es un sistema que funcione sin buscar la perfección absoluta.

Requisitos previos

Antes de comenzar, asegúrate de tener conocimientos básicos de Java, programación orientada a objetos, bases de datos relacionales (como relaciones uno a muchos) y HTML. Familiaridad con Spring será útil, aunque no indispensable. Además, necesitarás las siguientes herramientas instaladas:

  • Java Development Kit (JDK) 17 o superior
  • Maven como sistema de gestión de dependencias
  • Un entorno de desarrollo integrado (IDE) como IntelliJ IDEA
  • Navegador web para pruebas
  • Opcionalmente, Git para clonar el repositorio de ejemplo

¿Qué construiremos?

El objetivo es desarrollar un sistema de reservas para un complejo de apartamentos, donde los usuarios puedan iniciar sesión y reservar amenidades como gimnasio, piscina o sauna. Cada amenidad tiene una capacidad máxima para garantizar un uso seguro, especialmente en contextos como la pandemia de Covid-19. Las características principales incluyen:

  • Inicio de sesión para usuarios precreados (sin registro).
  • Visualización de reservas existentes por usuario.
  • Creación de nuevas reservas seleccionando tipo de amenidad, fecha y hora.
  • Restricción de acceso a páginas de reservas solo para usuarios autenticados.
  • Validación de capacidad para evitar reservas que excedan el límite.

Tecnologías utilizadas

El proyecto aprovechará varias tecnologías modernas para acelerar el desarrollo y garantizar un código limpio:

  • Spring Boot: Simplifica la configuración y desarrollo de aplicaciones Java.
  • Hibernate y JPA: Gestionan la persistencia de datos y mapeo objeto-relacional.
  • H2 Database: Base de datos en memoria para prototipos rápidos.
  • Thymeleaf: Motor de plantillas para renderizar vistas dinámicas.
  • Spring Security: Proporciona autenticación y autorización robustas.
  • Maven: Gestiona dependencias y compilación del proyecto.
  • Bootify: Genera código inicial para reducir configuraciones manuales.
  • Swagger: Documenta y prueba APIs automáticamente.
  • Bootstrap: Framework CSS para interfaces responsivas.
  • Lombok: Reduce código repetitivo mediante anotaciones.

Por qué elegir Spring Boot

Spring Boot es ideal para proyectos empresariales, pero también es eficiente para prototipos rápidos. Sus ventajas incluyen:

  • Desarrollo basado en anotaciones que genera código automáticamente.
  • Soporte para bases de datos en memoria como H2, eliminando la necesidad de configuraciones complejas.
  • Ecosistema maduro con abundante documentación y soporte comunitario.
  • Configuración mínima gracias a la eliminación de archivos XML complejos.
  • Spring Security integrado para aplicaciones seguras.
  • Automatización de tareas comunes, permitiendo enfocarse en la lógica de negocio.

Crear el proyecto con Bootify

Bootify es una herramienta freemium que genera código inicial para Spring Boot, ahorrando tiempo en configuraciones. Para iniciar, visita el sitio de Bootify y selecciona:

  • Tipo de compilación: Maven
  • Versión de Java: 17
  • Habilitar Lombok: Sí
  • Base de datos: H2
  • Añadir fechas de creación/actualización: Sí
  • Paquetes: Technical
  • Habilitar OpenAPI/Swagger: Sí
  • Dependencias adicionales: spring-boot-devtools

Define las entidades del proyecto:

  1. Reservation: Almacena datos de la reserva (fecha, hora de inicio, hora de fin, usuario).
  2. User: Representa a los usuarios con relación a sus reservas.
  3. AmenityType: Enum para tipos de amenidades (piscina, sauna, gimnasio).

Crea una relación muchos-a-uno entre Reservation y User, asegurando que cada reserva pertenezca a un solo usuario. Descarga el proyecto generado y ábrelo en tu IDE. La estructura inicial será similar a:

├── pom.xml
├── src
│   └── main
│       ├── java
│       │   └── com.amenity_reservation_system
│       │       ├── AmenityReservationSystemApplication.java
│       │       ├── model
│       │       │   ├── Reservation.java
│       │       │   ├── User.java
│       │       ├── repos
│       │       │   ├── ReservationRepository.java
│       │       │   ├── UserRepository.java
│       └── resources
│           └── application.yml

Explorar el código generado

El código generado incluye capas bien definidas:

  • Repositorios: Extienden JpaRepository para consultas automáticas a la base de datos. Por ejemplo, UserRepository permite buscar usuarios por ID o nombre de usuario.
public interface UserRepository extends JpaRepository<User, Long> {
    User findUserByUsername(String username);
}
  • Modelos: Definen entidades como User y Reservation, mapeadas a tablas de base de datos mediante Hibernate.
@Entity
@Getter
@Setter
public class Reservation {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    @Column(nullable = false)
    private LocalDate reservationDate;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", nullable = false)
    private User user;
}
  • Servicios: Contienen la lógica de negocio, manteniendo los controladores ligeros.
  • Controladores: Gestionan solicitudes HTTP y devuelven vistas o respuestas JSON.

Para probar el código, ejecuta la aplicación y accede a http://localhost:8080/swagger-ui/index.html para explorar la API generada con Swagger.

Configurar la base de datos H2

H2 es una base de datos en memoria ideal para prototipos. Configura application.yml para habilitar la consola de H2:

spring:
    datasource:
        url: jdbc:h2:mem:amenity-reservation-system
        username: sa
        password:
    jpa:
        hibernate:
            ddl-auto: update
    h2:
        console:
            enabled: true

Accede a la consola en http://localhost:8080/h2-console para verificar datos. Usa el usuario “sa” sin contraseña.

Ajustar el código generado

El código generado está orientado a APIs REST, pero nuestro proyecto usa vistas MVC. Elimina las carpetas model y rest para evitar conflictos con DTOs y controladores REST:

rm -rf src/main/java/com/amenity_reservation_system/model
rm -rf src/main/java/com/amenity_reservation_system/rest

Actualiza los servicios para usar entidades directamente:

@Service
public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User get(Long id) {
        return userRepository.findById(id)
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
    }
}

Crear vistas con Thymeleaf

Thymeleaf permite crear interfaces dinámicas. Agrega la dependencia en pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

Crea un directorio templates bajo src/main/resources y añade index.html:

<!DOCTYPE html>
<html lang="es" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8" />
        <title>Sistema de Reservas</title>
        <link
            th:rel="stylesheet"
            th:href="@{/webjars/bootstrap/4.0.0-2/css/bootstrap.min.css}"
        />
    </head>
    <body>
        <div class="container">
            <h1>Bienvenido al Sistema de Reservas</h1>
            <a href="/reservations" class="btn btn-success">Reservar Ahora</a>
        </div>
        <script th:src="@{/webjars/jquery/3.0.0/jquery.min.js}"></script>
        <script
            th:src="@{/webjars/bootstrap/4.0.0-2/js/bootstrap.min.js}"
        ></script>
    </body>
</html>

Crea un controlador para renderizar la vista:

@Controller
public class HomeController {
    @GetMapping("/")
    public String index(Model model) {
        return "index";
    }
}

Definir tipos de amenidades

Para gestionar tipos de amenidades, crea un enum AmenityType:

public enum AmenityType {
    POOL("Piscina"), SAUNA("Sauna"), GYM("Gimnasio");

    private final String name;

    AmenityType(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return name;
    }
}

Actualiza Reservation para incluir el tipo de amenidad:

@Enumerated(EnumType.STRING)
@Column(nullable = false)
private AmenityType amenityType;

Mostrar reservas de usuarios

Para cargar datos iniciales, usa CommandLineRunner en la clase principal:

@Bean
public CommandLineRunner loadData(UserRepository userRepository, ReservationRepository reservationRepository) {
    return args -> {
        User user = userRepository.save(new User("Juan Pérez", "juanp", "password"));
        Reservation reservation = Reservation.builder()
                .reservationDate(LocalDate.now())
                .startTime(LocalTime.of(12, 0))
                .endTime(LocalTime.of(13, 0))
                .amenityType(AmenityType.POOL)
                .user(user)
                .build();
        reservationRepository.save(reservation);
    };
}

Crea una vista reservations.html para mostrar reservas:

<!DOCTYPE html>
<html lang="es" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8" />
        <title>Reservas</title>
        <link
            th:rel="stylesheet"
            th:href="@{/webjars/bootstrap/4.0.0-2/css/bootstrap.min.css}"
        />
    </head>
    <body>
        <div class="container">
            <h3>Bienvenido <span th:text="${session.user.fullName}"></span></h3>
            <table class="table">
                <thead>
                    <tr>
                        <th>Amenidad</th>
                        <th>Fecha</th>
                        <th>Hora Inicio</th>
                        <th>Hora Fin</th>
                    </tr>
                </thead>
                <tbody>
                    <tr th:each="reservation : ${session.user.reservations}">
                        <td th:text="${reservation.amenityType}"></td>
                        <td th:text="${reservation.reservationDate}"></td>
                        <td th:text="${reservation.startTime}"></td>
                        <td th:text="${reservation.endTime}"></td>
                    </tr>
                </tbody>
            </table>
        </div>
    </body>
</html>

Actualiza el controlador para manejar sesiones:

@GetMapping("/reservations")
public String reservations(Model model, HttpSession session) {
    User user = userService.get(10000L);
    session.setAttribute("user", user);
    model.addAttribute("reservation", new Reservation());
    return "reservations";
}

Crear nuevas reservas

Añade un formulario en reservations.html para crear reservas usando un modal de Bootstrap:

<button
    type="button"
    class="btn btn-primary"
    data-toggle="modal"
    data-target="#createReservationModal"
>
    Crear Reserva
</button>
<div
    th:insert="fragments/modal :: modal"
    th:with="reservation=${reservation}"
></div>

Crea modal.html en templates/fragments:

<div
    class="modal fade"
    th:fragment="modal"
    id="createReservationModal"
    tabindex="-1"
    role="dialog"
>
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">Crear Reserva</h5>
                <button type="button" class="close" data-dismiss="modal">
                    &times;
                </button>
            </div>
            <div class="modal-body">
                <form
                    th:action="@{/reservations-submit}"
                    th:object="${reservation}"
                    method="post"
                >
                    <div class="form-group">
                        <label for="type-select">Amenidad</label>
                        <select
                            class="form-control"
                            id="type-select"
                            th:field="*{amenityType}"
                        >
                            <option value="POOL">Piscina</option>
                            <option value="SAUNA">Sauna</option>
                            <option value="GYM">Gimnasio</option>
                        </select>
                    </div>
                    <div class="form-group">
                        <label for="start-date">Fecha</label>
                        <input
                            class="form-control"
                            type="date"
                            id="start-date"
                            th:field="*{reservationDate}"
                        />
                    </div>
                    <button type="submit" class="btn btn-primary">
                        Guardar
                    </button>
                </form>
            </div>
        </div>
    </div>
</div>

Actualiza el controlador para procesar el formulario:

@PostMapping("/reservations-submit")
public String reservationsSubmit(@ModelAttribute Reservation reservation, @SessionAttribute("user") User user) {
    reservation.setUser(user);
    reservationService.create(reservation);
    user.getReservations().add(reservation);
    userService.update(user.getId(), user);
    return "redirect:/reservations";
}

Implementar autenticación con Spring Security

Añade dependencias de Spring Security en pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity5</artifactId>
    <version>3.0.4.RELEASE</version>
</dependency>

Configura la seguridad en WebSecurityConfig.java:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    private final UserDetailsServiceImpl userDetailsService;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;

    public WebSecurityConfig(UserDetailsServiceImpl userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) {
        this.userDetailsService = userDetailsService;
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/", "/webjars/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin().permitAll()
            .and()
            .logout().permitAll().logoutSuccessUrl("/");
    }
}

Crea UserDetailsServiceImpl para autenticación:

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    private final UserRepository userRepository;

    public UserDetailsServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findUserByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException(username);
        }
        return User.withUsername(user.getUsername())
                .password(user.getPasswordHash())
                .roles("USER")
                .build();
    }
}

Actualiza User con campos para autenticación:

@Column(nullable = false, unique = true)
private String username;

@Column
private String passwordHash;

Mostrar reservas del usuario autenticado

Modifica el controlador para usar el usuario autenticado:

@GetMapping("/reservations")
public String reservations(Model model, HttpSession session) {
    UserDetails principal = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    User user = userService.getUserByUsername(principal.getUsername());
    session.setAttribute("user", user);
    model.addAttribute("reservation", new Reservation());
    return "reservations";
}

Añade en UserService:

public User getUserByUsername(String username) {
    return userRepository.findUserByUsername(username);
}

Controlar la capacidad de amenidades

Crea una entidad Capacity para gestionar límites:

@Entity
@Getter
@Setter
public class Capacity {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false, unique = true)
    private AmenityType amenityType;

    @Column(nullable = false)
    private int capacity;
}

Crea CapacityRepository:

public interface CapacityRepository extends JpaRepository<Capacity, Long> {
    Capacity findByAmenityType(AmenityType amenityType);
}

Inicializa capacidades en AmenityReservationSystemApplication:

private Map<AmenityType, Integer> initialCapacities = new HashMap<>() {{
    put(AmenityType.GYM, 20);
    put(AmenityType.POOL, 4);
    put(AmenityType.SAUNA, 1);
}};

@Bean
public CommandLineRunner loadData(UserRepository userRepository, CapacityRepository capacityRepository) {
    return args -> {
        for (AmenityType type : initialCapacities.keySet()) {
            capacityRepository.save(new Capacity(type, initialCapacities.get(type)));
        }
    };
}

Actualiza ReservationService para verificar capacidad:

public Long create(Reservation reservation) {
    int capacity = capacityRepository.findByAmenityType(reservation.getAmenityType()).getCapacity();
    int overlapping = reservationRepository.findReservationsByReservationDateAndStartTimeBeforeAndEndTimeAfterOrStartTimeBetween(
            reservation.getReservationDate(),
            reservation.getStartTime(), reservation.getEndTime(),
            reservation.getStartTime(), reservation.getEndTime()
    ).size();

    if (overlapping >= capacity) {
        throw new RuntimeException("Capacidad completa para esta amenidad");
    }

    return reservationRepository.save(reservation).getId();
}

Conclusiones

Este tutorial demuestra cómo construir un sistema de reservas funcional con Spring Boot, integrando herramientas modernas como Hibernate, Thymeleaf y Spring Security. Desde la generación inicial del proyecto con Bootify hasta la implementación de autenticación y control de capacidad, el enfoque muestra la rapidez y robustez de Spring Boot para prototipos y aplicaciones escalables. Puedes encontrar el código completo en el repositorio de GitHub correspondiente. Continúa explorando Spring Boot para optimizar tus proyectos de desarrollo web.