====== 07 - Gestión de proyectos en Java: Paquetes, librerías y Maven ======
En la programación en Java, el reuso de código se articula mediante **librerías** y **paquetes**. Una librería es un conjunto de clases e interfaces organizadas en paquetes que proveen métodos y funcionalidades listas para usar, evitando la necesidad de reimplementarlas desde cero.
Por ejemplo, al invocar ''IO.println()'', se está haciendo uso de la clase ''IO'' dentro del paquete ''java.lang'' (perteneciente a la librería estándar de Java, importada por defecto). Si deseamos utilizar estructuras como ''List'' o ArrayList, debemos importar explícitamente el paquete ''java.util''.
package ejemplos.T09;
import java.util.ArrayList;
import java.util.List;
public class Librerias {
static void main() {
List numeros = new ArrayList<>();
IO.println("Hola mundo");
}
}
===== 1. Paquetes =====
Los **paquetes** (''package'') son la estructura organizativa básica de Java para evitar conflictos de nombres y gestionar el espacio de nombres.
Un paquete se corresponde directamente con una estructura de directorios en el sistema de archivos. Los niveles jerárquicos se separan mediante puntos. Por ejemplo, la declaración:
package es.cesguiro.foo;
exige que el archivo .java correspondiente esté ubicado en la ruta (dentro del proyecto):
es/cesguiro/foo
==== Importar paquetes ====
Para utilizar las clases públicas definidas en un paquete externo dentro de nuestro archivo de código, empleamos la sentencia ''import''. Existen distintas variantes según el nivel de precisión o el elemento que se desee importar:
=== Importación de una clase o interfaz concreta ===
Es la opción recomendada. Se especifica de forma explícita el paquete y el nombre exacto del elemento que se va a utilizar:
import java.util.Optional;
=== Importación completa ===
Permite importar **todas** las clases e interfaces públicas pertenecientes a un mismo paquete utilizando el operador asterisco (''*''). No afecta negativamente al rendimiento en tiempo de ejecución, pero puede provocar colisiones si dos paquetes importados contienen clases con el mismo nombre (como ''java.util.Date'' y ''java.sql.Date''):
import java.util.*;
=== Importación estática ===
Permite acceder a miembros estáticos (métodos o constantes de clase) sin necesidad de anteponer el nombre de la clase que los contiene. Simplifica la sintaxis en operaciones matemáticas o constantes globales:
import java.lang.Math.PI;
=== Importación de módulos completa ===
A partir de Java 23+, se introdujo la posibilidad de importar un módulo completo en una sola línea mediante import module. Esto pone a disposición del archivo de código todos los paquetes exportados por dicho módulo, evitando declarar múltiples sentencias ''import'' individuales:
import module java.base;
Introducido en Java 9 como el **JPMS** (**Java Platform Module System**), un **módulo** es un nivel de abstracción por encima de los paquetes.\\ \\
* **Paquete**: Agrupa clases e interfaces relacionadas dentro del código.\\ \\
* **Módulo**: Es un contenedor con nombre que agrupa uno o más paquetes, junto con un archivo de descriptor (module-info.java) que especifica qué paquetes exporta para que otros los usen y qué otros módulos requiere para funcionar.\\ \\
Por ejemplo, el módulo estándar ''java.base'' incluye internamente los paquetes ''java.util'', ''java.io'', ''java.lang'', ''java.math'', etc. Mientras que la cláusula ''import java.util.*;'' solo importa las clases de ese paquete concreto, ''import module java.base;'' importa todos los paquetes expuestos por ese módulo.
===== 2. Librerías externas y archivos JAR =====
Una librería en Java es una colección de paquetes compilados que implementan funcionalidades específicas para que puedan ser reutilizadas en múltiples aplicaciones sin necesidad de volver a escribir el código desde cero.
Existen dos grandes categorías de librerías:
* **Librería estándar de Java (JDK)**: Incluida en el propio entorno de desarrollo (paquetes como ''java.lang'', ''java.util'', ''java.io'', ''java.net'' o ''java.math'').\\ \\
* **Librerías de terceros (third-party)**: Desarrolladas por la comunidad o por empresas externas para resolver problemas específicos (procesamiento de JSON, persistencia de datos, pruebas unitarias, etc.).\\ \\
==== El formato JAR (Java ARchive) ====
Las librerías en Java se distribuyen y consumen habitualmente empaquetadas en archivos con extensión ''.jar''.
Un archivo JAR es un contenedor comprimido (basado en el formato estándar ZIP) que agrupa dentro de una misma estructura:
* Los archivos de bytecode compilado (''.class'').\\ \\
* Los recursos asociados (archivos de propiedades, esquemas XML, imágenes).\\ \\
* Un archivo de metadatos obligatorio llamado ''META-INF/MANIFEST.MF'', que indica la versión de la librería, el compilador utilizado y, opcionalmente, la clase principal si se trata de un ejecutable.\\ \\
==== El Classpath (Ruta de clases) ====
Para que la Máquina Virtual de Java (JVM) o el compilador (''javac'') puedan utilizar los tipos y métodos contenidos en una librería externa ''.jar'', dicho archivo debe ser incluido explícitamente en el **Classpath**.
El //Classpath// es una lista de rutas de carpetas o archivos ''.jar'' en la que Java busca las definiciones de clases cuando procesa las sentencias ''import'' de nuestro código.
En proyectos tradicionales (sin gestor de dependencias), los archivos ''.jar'' de terceros suelen colocarse en una carpeta llamada ''lib/'' en la raíz del proyecto y añadirse a la configuración de compilación de la aplicación.
Por estas razones, el desarrollo profesional con librerías externas se gestiona mediante **gestores de proyectos y dependencias** como Apache Maven.
Aquí tienes el apartado completo sobre Gestores de Proyectos adaptado al formato DokuWiki, manteniendo la distinción clara entre la gestión completa del proyecto y la gestión de dependencias, y libre de cualquier referencia a entornos de desarrollo específicos como VSCode.
===== 3. Gestores de Proyectos =====
Un **gestor de proyectos** (o herramienta de construcción / build tool) es una herramienta de software que automatiza y estandariza todo el ciclo de vida del desarrollo de una aplicación: desde la compilación del código fuente y la ejecución de pruebas unitarias, hasta el empaquetado final y la distribución del software.
Una de las funcionalidades más importantes (y conocidas) dentro de un gestor de proyectos es la **gestión de dependencias**, la cual se encarga de localizar, descargar, actualizar y resolver de forma automática todas las librerías externas que el proyecto necesita para su compilación y ejecución.
En proyectos reales, la gestión manual de archivos ''.jar'' resulta insostenible debido a:
* **Dependencias transitivas**: Si una librería A necesita de la librería B para funcionar, la herramienta descarga automáticamente ambas sin intervención manual.\\ \\
* **Control de versiones dinámico**: Cambiar la versión de una librería solo requiere modificar una línea en el archivo de configuración.\\ \\
* **Estandarización del entorno**: Todo el equipo de desarrollo compila y construye el proyecto exactamente de la misma forma, independientemente del //IDE// o sistema operativo utilizado.\\ \\
En el ecosistema Java conviven principalmente tres gestores de proyectos:
* **Ant**: El gestor histórico. Basado en archivos XML, no impone ninguna estructura ni convención previa. El desarrollador debe programar manualmente cada tarea de compilación.\\ \\
* **Maven**: Evolución basada en el principio de Convención sobre Configuración. Impone una estructura de directorios estándar y automatiza el ciclo de vida del proyecto mediante repositorios centralizados.\\ \\
* **Gradle**: Herramienta moderna que utiliza un lenguaje específico de dominio (DSL basado en Groovy o Kotlin) en lugar de XML. Muy extendido en entornos multiproyecto y desarrollo Android.\\ \\
===== 4. Apache Maven =====
**Maven** es un gestor de proyectos integral desarrollado por la **Apache Software Foundation**.
==== Estructura Estándar de Carpetas ====
Maven impone la regla de Convención sobre Configuración: si respetas la organización predefinida de archivos, no necesitas configurar las rutas de compilación manualmente.
El directorio ''target/'' se genera automáticamente durante el proceso de construcción para almacenar las clases compiladas (''.class'') y el artefacto final (''.jar'').
==== El archivo POM (Project Object Model) ====
El archivo ''pom.xml'' ubicado en la raíz del proyecto es el corazón de Maven. Contiene toda la información del proyecto, incluyendo metadatos, propiedades de compilación y la lista de dependencias externas.
=== Encabezado XML y Coordenadas GAV ===
Todo documento //POM// comienza con la declaración del estándar //XML// y la definición del modelo (''modelVersion 4.0.0''). A continuación se declaran las coordenadas únicas del artefacto:
* **groupId**: Identificador de la organización o paquete base (ej: es.cesguiro).\\ \\
* **artifactId**: Nombre único del proyecto o módulo (ej: ejemplo-maven).\\ \\
* **version**: Versión actual de desarrollo o despliegue (ej: 0.0.1-SNAPSHOT).\\ \\
es.cesguiro
ejemplo-maven
0.0.1-SNAPSHOT
=== Metadatos del Proyecto (Opcionales) ===
Es buena práctica incluir información descriptiva de la aplicación. Esta información es fundamental cuando publicamos librerías para que otros desarrolladores conozcan su propósito, licencia y autoría. Los elementos opcionales más comunes son:
'''': Nombre legible del proyecto para mostrar en documentación o informes.\ \
'''': Resumen o descripción del propósito del proyecto.\ \
'''': Sitio web oficial o página de documentación del proyecto.\ \
'''': Licencia bajo la cual se distribuye el software (ej: Apache 2.0, MIT).\ \
'''': Información sobre los autores o mantenedores del código.\ \
'''': Configuración del control de versiones (Software Configuration Management), indicando la ruta del repositorio Git.\ \
Apache License, Version 2.0
[https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt)
cesguiro
César Guijarro
cesar.guijarrorosalney@edu.gva.es
scm:git:git://[github.com/cesguiro/ejemplo-maven.git](https://github.com/cesguiro/ejemplo-maven.git)
[https://github.com/cesguiro/ejemplo-maven](https://github.com/cesguiro/ejemplo-maven)
=== Propiedades del Proyecto () ===
El apartado ''properties'' permite definir constantes globales para ser reutilizadas a lo largo del fichero ''pom.xml'', así como configurar variables internas de Maven (como la versión de Java a compilar o la codificación de caracteres):
...
25
2.10.1
=== Gestión de Dependencias () ===
En esta sección se listan los archivos ''.jar'' externos que requiere la aplicación. Maven se encarga de descargarlos desde el repositorio remoto y resolver sus dependencias transitivas. Para referenciar una variable declarada en '''', se utiliza la sintaxis ''${nombre.variable}'':
org.junit.jupiter
junit-jupiter-api
5.10.0
test
com.google.code.gson
gson
${gson.version}
La etiqueta '''' define en qué momentos del ciclo de vida del proyecto estará disponible la librería:
* ''compile'' (valor por defecto si se omite): La dependencia está disponible en la compilación, ejecución y empaquetado del proyecto.\\ \\
* ''test'': La librería solo se compila y ejecuta durante la fase de pruebas. No se incluirá en el archivo JAR final distribuido a producción.\\ \\
* ''provided'': Se utiliza cuando se necesita la librería para compilar, pero no para empaquetar porque el entorno donde se ejecutará (servidor de aplicaciones como Tomcat) ya la incluye.\\ \\
* ''runtime'': La dependencia no es necesaria para compilar, pero sí para la ejecución de la aplicación (común en controladores de bases de datos JDBC).\\ \\
=== Configuración de Construcción () ===
En la etiqueta '''' se configura el comportamiento del proceso de empaquetado, la personalización del nombre del archivo resultante y el uso de plugins que extienden el ciclo de vida de compilación (como ajustar el plugin del compilador o empaquetar un archivo JAR ejecutable con todas sus dependencias incorporadas):
org.apache.maven.plugins
maven-compiler-plugin
3.11.0
${maven.compiler.release}
==== Sistema de Repositorios ====
Maven automatiza la descarga de librerías organizando los recursos en tres tipos de repositorios:
* **Repositorio Local**: Ubicado por defecto en ''/.m2/repository''. Es la caché local de tu máquina.\\ \\
* **Repositorio Central ([[https://mvnrepository.com/|mvnrepository]])**: Servidor público remoto administrado por Apache que aloja millones de librerías de código abierto.\\ \\
* **Repositorios Remotos Privados**: Servidores empresariales (como Nexus) para alojar artefactos internos de una compañía.
Cuando compilas un proyecto, Maven busca cada dependencia declarada en el ''pom.xml'' dentro del repositorio local (''.m2''). Si no la encuentra, la descarga automáticamente desde //Maven Central//, la guarda en el disco local para uso futuro y la inyecta en el //classpath//.
==== Ciclo de Vida del Proyecto (Build Lifecycle) ====
Maven organiza el proceso de construcción en tres ciclos de vida independientes: ''clean'' (limpieza), ''default'' (construcción) y ''site'' (documentación).
Dentro de un mismo ciclo de vida, al ejecutar una fase, Maven ejecutará automáticamente todas las fases anteriores de dicho ciclo.
Las fases más importantes del ciclo ''default'' (en orden de ejecución) son:
* **validate**: Comprueba que el proyecto es correcto y que toda la información necesaria está disponible.\\ \\
* **compile**: Compila el código fuente del proyecto (''src/main/java'') y deposita los ''.class'' en ''target/classes''.\\ \\
* **test**: Compila y ejecuta las pruebas unitarias (''src/test/java'').\\ \\
* **package**: Toma el código compilado y lo empaqueta en su formato de distribución (como un archivo ''.jar'').\\ \\
* **verify**: Ejecuta pruebas de integración y comprueba que el paquete cumple los criterios de calidad.\\ \\
* **install**: Copia el archivo ''.jar'' generado en el repositorio local (''~/.m2/repository''), permitiendo que otros proyectos locales puedan usarlo como dependencia.\\ \\
* **deploy**: Copia el paquete final a un repositorio remoto compartido para distribuirlo con otros desarrolladores.\\ \\
Además, el ciclo ''clean'' cuenta con la fase:
* **clean**: Borra el directorio de salida (''target/'') para garantizar una compilación limpia desde cero.
Las órdenes de construcción se invocan desde la interfaz de línea de comandos indicando las fases a ejecutar:
mvn clean install
Aunque las fases pueden ejecutarse desde la terminal, la mayoría de entornos de desarrollo como //IntelliJ IDEA//, //Eclipse// o //VS Code// incorporan paneles integrados de Maven. Desde estas herramientas es posible ejecutar cualquier fase (como ''clean'' o ''package'') simplemente haciendo doble clic sobre el nombre del ciclo de vida en la barra lateral del IDE, sin necesidad de escribir comandos de terminal.
===== Ejercicios =====
** Ejercicio 1 **
Crea un proyecto //Maven// que contenga 2 paquetes (dentro del paquete original):
* **back**
* **front**
En el paquete //front// crea dos clases:
* **Menu**: Esta clase tendrá un solo método (//show()//), que mostrará por pantalla el menú principal de la aplicación:
1.- Listado clientes
2.- Buscar cliente
0.- Salir
----------------------
Opción:
* **App**: Será nuestra clase principal. El método //main()// mostrará el menú anterior hasta que se introduzca el 0. Después de leer la opción escogida por el usuario, llamará a otro método (**request()**) que, por ahora, mostrará la frase "//haciendo petición al servidor...//".
** Ejercicio 2 **
En el paquete //back// crea una clase llamada **App** (mismo nombre que en el //front//). Esta clase tendrá un método estático //response()// que devolverá la frase "//Respuesta del servidor...//".
Modifica el método //request()// de la clase //front.App// para que muestre por pantalla el resultado de la función //back.App.response()//.
Como verás, Java da un error al usar nombres de clases iguales (//App//). Averigua como solucionar el conflicto entre las clases //front.App// y //front.back//
** Ejercicio 3 **
Crea la clase **back.controller.CustomerController**. Dentro de esta clase, crea el método //findAll()//, el cuál devolverá la frase "//Listado de todos los clientes//".
Haz que el método //back.App.response()// llame al método anterior si la opción elegida por el usuario es el 1 y la frase "//404 .- Recurso no encontrado//" si se le pasa cualquier otra opción.
** Ejercicio 4 **
Crea la clase **back.domain.Customer** con tres propiedades privadas: //id//, //name// y //nif//, los //getters// correspondientes y el método //toString()//.
Crea una segunda clase **back.domain.CustomerService**. Esta clase tendrá un método //findAll()// que creará un listado de clientes (//List//) y lo devolverá (crea tres o cuatro clientes en el listado para probar).
Haz que el método //back.controller.CustomerController.findAll()// devuelva el listado de clientes generado por el método //back.domain.CustomerService.findAll()//.
El resultado deberá ser algo similar a ésto:
Opción: 1
Clientes:[ {1, Cliente 1, A11111111}, {2, Cliente 2, B22222222}, {3, Cliente 3, C22222222}, {4, Cliente 4, D22222222} ]
1.- Listado clientes
** Ejercicio 5 **
Crea una última clase **back.persistence.CustomerDao** con el método //findAll()//. Traslada la creación del listado de clientes (básicamente la implementación del método //back.domain.CustomerService.findAll()//) a este método y haz que la clase //CustomerService// llame al método //back.persistence.CustomerDao.findAll()// y devuelva el resultado.
** Ejercicio 6 **
Implementa la funcionalidad de buscar un cliente por //id// siguiendo la misma filosofía que la anterior (devolver el listado de todos los clientes).