Saltar a contenido

Semana 13: Gestores de Dependencias en Java con VS Code

Introducción

Los gestores de dependencias son herramientas fundamentales en el desarrollo de Java que nos permiten:

  • Gestionar librerías externas de forma automática
  • Resolver conflictos entre versiones de dependencias
  • Automatizar la construcción del proyecto
  • Mantener la consistencia entre diferentes entornos de desarrollo

En esta semana exploraremos los dos gestores más populares: Maven y Gradle, utilizando las extensiones de Visual Studio Code.

Extensiones de VS Code para Java

Extension Pack for Java

La extensión principal que necesitamos es Extension Pack for Java, que incluye:

  • Language Support for Java™ by Red Hat
  • Debugger for Java
  • Test Runner for Java
  • Maven for Java
  • Project Manager for Java
  • Visual Studio IntelliCode

Extensiones Adicionales Recomendadas

  • Gradle for Java: Soporte específico para proyectos Gradle
  • Spring Boot Extension Pack: Para proyectos Spring Boot
  • Lombok Annotations Support for VS Code: Soporte mejorado para Lombok

Maven: Gestión de Dependencias

Estructura de un Proyecto Maven

mi-proyecto-maven/
├── pom.xml
├── src/
│   ├── main/
│   │   └── java/
│   │       └── com/
│   │           └── ejemplo/
│   │               └── App.java
│   └── test/
│       └── java/
│           └── com/
│               └── ejemplo/
│                   └── AppTest.java
└── target/

Configuración del archivo pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <!-- Información del proyecto -->
    <groupId>com.ejemplo</groupId>
    <artifactId>mi-proyecto-maven</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <name>Mi Proyecto Maven</name>
    <description>Ejemplo de proyecto Maven con Lombok</description>

    <!-- Propiedades del proyecto -->
    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <lombok.version>1.18.30</lombok.version>
    </properties>

    <!-- Dependencias -->
    <dependencies>
        <!-- Lombok para reducir código boilerplate -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <!-- Configuración de plugins -->
    <build>
        <plugins>
            <!-- Plugin del compilador -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>${lombok.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Uso de Maven en VS Code

  1. Crear proyecto: Usar Command Palette (Ctrl+Shift+P) → "Java: Create Java Project" → "Maven"
  2. Gestión de dependencias:
  3. Editar pom.xml
  4. VS Code detecta automáticamente los cambios
  5. Usar "Java: Reload Projects" si es necesario
  6. Comandos Maven: Disponibles en la paleta de comandos con prefijo "Maven:"

Ejemplo Práctico con Maven y Lombok

Clase de Ejemplo con Lombok (Maven)

package com.ejemplo.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;


import java.time.LocalDateTime;
import java.util.List;

/**
 * Clase que representa un Usuario del sistema
 * Utiliza Lombok para reducir código boilerplate
 * Configurado para proyecto Maven
 */
@Data                    // Genera getters, setters, toString, equals y hashCode
@NoArgsConstructor       // Genera constructor sin parámetros
@AllArgsConstructor      // Genera constructor con todos los parámetros
@Builder                 // Genera patrón Builder
public class Usuario {

    private Long id;
    private String nombre;
    private String email;
    private LocalDateTime fechaCreacion;
    private List<String> roles;
    private boolean activo;

    /**
     * Método personalizado que utiliza el logger de Lombok
     */
    public void saludar() {
        System.out.println("Hola, soy " + nombre + " con email " + email);
    }

    /**
     * Método que demuestra el uso del builder
     */
    public static Usuario crearUsuarioEjemplo() {
        return Usuario.builder()
                .nombre("Juan Pérez")
                .email("juan.perez@ejemplo.com")
                .fechaCreacion(LocalDateTime.now())
                .roles(List.of("USER", "ADMIN"))
                .activo(true)
                .build();
    }
}

Clase Principal de Ejemplo (Maven)

package com.ejemplo;

import com.ejemplo.model.Usuario;

import java.time.LocalDateTime;
import java.util.Arrays;

/**
 * Clase principal que demuestra el uso de Lombok en Maven
 */
public class App {

    public static void main(String[] args) {
        System.out.println("Iniciando aplicación Maven con Lombok");

        // Ejemplo 1: Usando constructor sin parámetros y setters
        Usuario usuario1 = new Usuario();
        usuario1.setNombre("María García");
        usuario1.setEmail("maria.garcia@ejemplo.com");
        usuario1.setFechaCreacion(LocalDateTime.now());
        usuario1.setActivo(true);

        System.out.println("Usuario 1 creado: " + usuario1);

        // Ejemplo 2: Usando constructor con todos los parámetros
        Usuario usuario2 = new Usuario(
            2L,
            "Carlos López",
            "carlos.lopez@ejemplo.com",
            LocalDateTime.now(),
            Arrays.asList("USER"),
            true
        );

        System.out.println("Usuario 2 creado: " + usuario2);

        // Ejemplo 3: Usando el patrón Builder
        Usuario usuario3 = Usuario.builder()
                .id(3L)
                .nombre("Ana Martínez")
                .email("ana.martinez@ejemplo.com")
                .fechaCreacion(LocalDateTime.now())
                .roles(Arrays.asList("USER", "MODERATOR"))
                .activo(true)
                .build();

        System.out.println("Usuario 3 creado con Builder: " + usuario3);

        // Ejemplo 4: Usando método estático
        Usuario usuario4 = Usuario.crearUsuarioEjemplo();
        usuario4.saludar();

        // Demostrando métodos generados automáticamente
        demostrarMetodosLombok(usuario1, usuario2, usuario3, usuario4);
    }

    private static void demostrarMetodosLombok(Usuario... usuarios) {
        System.out.println("=== Demostrando métodos generados por Lombok en Maven ===");

        for (Usuario usuario : usuarios) {
            // toString() generado automáticamente
            System.out.println("toString(): " + usuario.toString());

            // Getters generados automáticamente
            System.out.println("Nombre: " + usuario.getNombre() + 
                    ", Email: " + usuario.getEmail() + 
                    ", Activo: " + usuario.isActivo());
        }

        // equals() y hashCode() generados automáticamente
        Usuario usuarioCopia = new Usuario();
        usuarioCopia.setNombre(usuarios[0].getNombre());
        usuarioCopia.setEmail(usuarios[0].getEmail());
        usuarioCopia.setFechaCreacion(usuarios[0].getFechaCreacion());
        usuarioCopia.setActivo(usuarios[0].isActivo());

        System.out.println("¿Son iguales usuario1 y usuarioCopia? " + 
                usuarios[0].equals(usuarioCopia));
    }
}

Configuración de VS Code para Maven y Lombok

Configuración específica para Maven en settings.json

{
    "java.configuration.updateBuildConfiguration": "automatic",
    "java.compile.nullAnalysis.mode": "automatic",
    "java.saveActions.organizeImports": true,
    "java.format.settings.url": "https://raw.githubusercontent.com/google/styleguide/gh-pages/eclipse-java-google-style.xml",
    "lombok.enabled": true,
    "maven.executable.path": "auto",
    "maven.terminal.useJavaHome": true,
    "maven.view": "hierarchical"
}

Configuración en launch.json para debugging con Maven

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "java",
            "name": "Launch App (Maven)",
            "request": "launch",
            "mainClass": "com.ejemplo.App",
            "projectName": "mi-proyecto-maven",
            "vmArgs": "-javaagent:${userHome}/.m2/repository/org/projectlombok/lombok/1.18.30/lombok-1.18.30.jar"
        }
    ]
}

Mejores Prácticas para Maven

  1. Usar propiedades para versiones de dependencias
  2. Definir encoding explícitamente
  3. Usar dependency management para proyectos multi-módulo
  4. Mantener pom.xml limpio y bien documentado
  5. Configurar annotation processors correctamente para Lombok
  6. Usar perfiles para diferentes entornos

Troubleshooting Maven

Problemas comunes con Maven y Lombok

  1. Lombok no funciona: Verificar que la extensión esté instalada y habilitada
  2. Errores de compilación: Asegurar que annotation processor esté configurado en el plugin del compilador
  3. Métodos no reconocidos: Recargar el proyecto Java con Ctrl+Shift+P → "Java: Reload Projects"
  4. Dependencias no se descargan: Verificar conexión y repositorios en pom.xml
  5. Versión de Java incorrecta: Revisar propiedades del compilador

Gradle: Gestión de Dependencias

Estructura de un Proyecto Gradle

mi-proyecto-gradle/
├── build.gradle
├── settings.gradle
├── gradle/
│   └── wrapper/
├── gradlew
├── gradlew.bat
└── src/
    ├── main/
    │   └── java/
    │       └── com/
    │           └── ejemplo/
    │               └── App.java
    └── test/
        └── java/
            └── com/
                └── ejemplo/
                    └── AppTest.java

Configuración del archivo build.gradle

plugins {
    id 'java'
    id 'application'
}

// Información del proyecto
group = 'com.ejemplo'
version = '1.0.0'
description = 'Ejemplo de proyecto Gradle con Lombok'

// Configuración de Java
java {
    sourceCompatibility = JavaVersion.VERSION_17
    targetCompatibility = JavaVersion.VERSION_17
}

// Configuración de la aplicación
application {
    mainClass = 'com.ejemplo.App'
}

// Repositorios donde buscar dependencias
repositories {
    mavenCentral()
}

// Versiones de dependencias
ext {
    lombokVersion = '1.18.30'
}

// Dependencias del proyecto
dependencies {
    // Lombok para reducir código boilerplate
    compileOnly "org.projectlombok:lombok:${lombokVersion}"
    annotationProcessor "org.projectlombok:lombok:${lombokVersion}"

    // Dependencias para testing
    testCompileOnly "org.projectlombok:lombok:${lombokVersion}"
    testAnnotationProcessor "org.projectlombok:lombok:${lombokVersion}"
}

// Configuración de tareas
tasks.named('test') {
    useJUnitPlatform()
}

// Configuración del compilador
tasks.withType(JavaCompile) {
    options.encoding = 'UTF-8'
    options.compilerArgs += ['-parameters']
}

// Configuración para generar JAR ejecutable
jar {
    manifest {
        attributes(
            'Main-Class': 'com.ejemplo.App'
        )
    }
}

Configuración del archivo settings.gradle

rootProject.name = 'mi-proyecto-gradle'

Uso de Gradle en VS Code

  1. Crear proyecto: Usar Command Palette → "Java: Create Java Project" → "Gradle"
  2. Gestión de dependencias:
  3. Editar build.gradle
  4. VS Code sincroniza automáticamente
  5. Usar "Java: Reload Projects" para forzar sincronización
  6. Comandos Gradle: Disponibles en la paleta de comandos con prefijo "Gradle:"

Ejemplo Práctico con Gradle y Lombok

Clase de Ejemplo con Lombok (Gradle)

package com.ejemplo.model;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Arrays;

/**
 * Clase que representa un Usuario del sistema
 * Utiliza Lombok para reducir código boilerplate
 * Configurado para proyecto Gradle
 */
@Data                    // Genera getters, setters, toString, equals y hashCode
@NoArgsConstructor       // Genera constructor sin parámetros
@AllArgsConstructor      // Genera constructor con todos los parámetros
@Builder                 // Genera patrón Builder
public class Usuario {

    private Long id;
    private String nombre;
    private String email;
    private LocalDateTime fechaCreacion;
    private List<String> roles;
    private boolean activo;

    /**
     * Método personalizado que utiliza el logger de Lombok
     */
    public void saludar() {
        System.out.println("Hola, soy " + nombre + " con email " + email);
    }

    /**
     * Método que demuestra el uso del builder
     */
    public static Usuario crearUsuarioEjemplo() {
        return Usuario.builder()
                .nombre("Ana Rodríguez")
                .email("ana.rodriguez@ejemplo.com")
                .fechaCreacion(LocalDateTime.now())
                .roles(List.of("USER", "DEVELOPER"))
                .activo(true)
                .build();
    }
}

Clase Principal de Ejemplo (Gradle)

package com.ejemplo;

import com.ejemplo.model.Usuario;

import java.time.LocalDateTime;
import java.util.Arrays;

/**
 * Clase principal que demuestra el uso de Lombok en Gradle
 */
public class App {

    public static void main(String[] args) {
        System.out.println("Iniciando aplicación Gradle con Lombok");

        // Ejemplo 1: Usando constructor sin parámetros y setters
        Usuario usuario1 = new Usuario();
        usuario1.setNombre("Pedro Martín");
        usuario1.setEmail("pedro.martin@ejemplo.com");
        usuario1.setFechaCreacion(LocalDateTime.now());
        usuario1.setActivo(true);

        System.out.println("Usuario 1 creado: " + usuario1);

        // Ejemplo 2: Usando constructor con todos los parámetros
        Usuario usuario2 = new Usuario(
            2L,
            "Laura Sánchez",
            "laura.sanchez@ejemplo.com",
            LocalDateTime.now(),
            Arrays.asList("USER", "TESTER"),
            true
        );

        System.out.println("Usuario 2 creado: " + usuario2);

        // Ejemplo 3: Usando el patrón Builder
        Usuario usuario3 = Usuario.builder()
                .id(3L)
                .nombre("Miguel Torres")
                .email("miguel.torres@ejemplo.com")
                .fechaCreacion(LocalDateTime.now())
                .roles(Arrays.asList("USER", "ARCHITECT"))
                .activo(true)
                .build();

        System.out.println("Usuario 3 creado con Builder: " + usuario3);

        // Ejemplo 4: Usando método estático
        Usuario usuario4 = Usuario.crearUsuarioEjemplo();
        usuario4.saludar();

        // Demostrando métodos generados automáticamente
        demostrarMetodosLombok(usuario1, usuario2, usuario3, usuario4);
    }

    private static void demostrarMetodosLombok(Usuario... usuarios) {
        System.out.println("=== Demostrando métodos generados por Lombok en Gradle ===");

        for (Usuario usuario : usuarios) {
            // toString() generado automáticamente
            System.out.println("toString(): " + usuario.toString());

            // Getters generados automáticamente
            System.out.println("Nombre: " + usuario.getNombre() + 
                    ", Email: " + usuario.getEmail() + 
                    ", Activo: " + usuario.isActivo());
        }

        // equals() y hashCode() generados automáticamente
        Usuario usuarioCopia = new Usuario();
        usuarioCopia.setNombre(usuarios[0].getNombre());
        usuarioCopia.setEmail(usuarios[0].getEmail());
        usuarioCopia.setFechaCreacion(usuarios[0].getFechaCreacion());
        usuarioCopia.setActivo(usuarios[0].isActivo());

        System.out.println("¿Son iguales usuario1 y usuarioCopia? " + 
                usuarios[0].equals(usuarioCopia));
    }
}

Configuración de VS Code para Gradle y Lombok

Configuración específica para Gradle en settings.json

{
    "java.configuration.updateBuildConfiguration": "automatic",
    "java.compile.nullAnalysis.mode": "automatic",
    "java.saveActions.organizeImports": true,
    "java.format.settings.url": "https://raw.githubusercontent.com/google/styleguide/gh-pages/eclipse-java-google-style.xml",
    "lombok.enabled": true,
    "gradle.nestedProjects": true,
    "gradle.synchronization.enabled": true,
    "gradle.experimental.refreshDependencies": true
}

Configuración en launch.json para debugging con Gradle

{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "java",
            "name": "Launch App (Gradle)",
            "request": "launch",
            "mainClass": "com.ejemplo.App",
            "projectName": "mi-proyecto-gradle",
            "classPaths": ["${workspaceFolder}/build/classes/java/main"],
            "vmArgs": "-javaagent:${userHome}/.gradle/caches/modules-2/files-2.1/org.projectlombok/lombok/1.18.30/lombok-1.18.30.jar"
        }
    ]
}

Mejores Prácticas para Gradle

  1. Usar ext block para variables globales
  2. Aprovechar los plugins oficiales
  3. Configurar wrapper para consistencia de versiones
  4. Usar buildSrc para lógica compleja de build
  5. Configurar annotation processors correctamente para Lombok
  6. Aprovechar builds incrementales para mejor rendimiento

Troubleshooting Gradle

Problemas comunes con Gradle y Lombok

  1. Lombok no funciona: Verificar que tanto compileOnly como annotationProcessor estén configurados
  2. Errores de compilación: Asegurar que annotation processor esté configurado para compile y test
  3. Métodos no reconocidos: Ejecutar ./gradlew clean build y recargar proyecto
  4. Build falla: Verificar sintaxis de build.gradle y compatibilidad de versiones
  5. Dependencias no resuelven: Revisar repositorios configurados y conectividad
  6. Wrapper no funciona: Verificar permisos de ejecución en gradlew

COMPARACIÓN Y CONCLUSIONES

Comparación: Maven vs Gradle

Aspecto Maven Gradle
Configuración XML (pom.xml) Groovy/Kotlin DSL (build.gradle)
Curva de aprendizaje Más fácil para principiantes Más flexible, requiere más conocimiento
Rendimiento Más lento en builds grandes Más rápido, builds incrementales
Ecosistema Muy maduro, amplia adopción Moderno, creciente adopción
Flexibilidad Menos flexible, más convenciones Muy flexible, altamente personalizable
Configuración Lombok Plugin del compilador compileOnly + annotationProcessor
Soporte VS Code Excelente, integración nativa Muy bueno, extensión adicional

Cuándo usar cada uno

Usar Maven cuando:

  • Proyectos empresariales con equipos grandes
  • Convenciones estrictas son preferibles
  • Compatibilidad con herramientas legacy es importante
  • Simplicidad en configuración es prioritaria

Usar Gradle cuando:

  • Rendimiento de build es crítico
  • Flexibilidad en configuración es necesaria
  • Proyectos Android (Gradle es el estándar)
  • Builds complejos con lógica personalizada