====== 11 - Librerías ====== En casi todos los lenguajes de programación existe el concepto de **librerías**. Una librería se puede entender como un conjunto de clases, que poseen una serie de métodos y atributos. Lo realmente interesante de estas librerías es que nos permiten reutilizar código, es decir que podemos hacer uso de los métodos, clases y atributos que componen la librería evitando así tener que implementar nosotros mismos esas funcionalidades. Por ejemplo, cuando queremos imprimir algo por pantalla estamos usando el método //System.out.print()//. En realidad, la clase //System// pertenece a la librería //java.lang//, que es la librería estándar de Java, con lo que no hace falta importarla. También hemos usado en nuestros ejemplos las clases //List// y //ArrayList// y sus métodos. Estas clases está definidas en la librería //java.util//, que, al no pertenecer a la librería estándar, tenemos que importar para poder utilizarla. package ejemplos.T09; import java.util.ArrayList; import java.util.List; public class Librerias { public static void main(String[] args) { List numeros = new ArrayList<>(); System.out.println("Hola mundo"); } } Lo más interesante de las librerías en Java, es que nosotros también podemos crearlas y hacer uso de ellas al interior de nuestros proyectos. Pero antes de ver como crear e importar librerías, necesitamos tener claro otro concepto relacionado: los **paquetes**. ===== Paquetes ===== Los paquetes (**package**) son el mecanismo que usa Java para facilitar la modularidad del código. Un paquete puede contener una o más definiciones de interfaces y clases, distribuyéndose habitualmente como un archivo. Para utilizar los elementos de un paquete es necesario importar este en el módulo de código en curso, usando para ello la sentencia **import**. Los paquetes Java equivalen a directorios. Es decir, cada miembro del paquete (separado por puntos) se traduce a un directorio en el sistema de archivos. Por ejemplo, un paquete declarado como: package net.fpmislata.graficos2d; debe almacenarse en: net\fpmislata\graficos2d ==== Crear paquetes ==== Para crear un paquete en Java, simplemente debemos definir su nombre al principio del fichero mediante la palabra reservada **package**. Como vimos en los primeros temas, este proceso lo podemos simplificar si utilizamos algún IDE. Por ejemplo, en VSC podemos usar la orden //New Package// para crear un nuevo paquete. Cualquier clase que creemos dentro de ese paquete ya tendrá incorporado la definición del paquete al principio del fichero: {{ :clase:daw:prog:1eval:vsc_20.png?400 |}} Por supuesto, puedes crear paquetes dentro de otros paquetes, generando así una estructura jerárquica de tus clases. El nombre de los subpaquetes (paquetes contenidos en otros) se compondrán del identificador del contenedor seguido de un punto y el nombre del subpaquete. De existir niveles adicionales se agregarían los distintos identificadores separados por puntos, formando así el nombre completo del paquete. A la hora de nombrar paquetes, normalmente se pone el nombre de dominio inverso de nivel superior. Por ejemplo, **com.fpmislata.nombrePaquete**. Esto se hace principalmente para que no coincida con los nombres de otros paquetes que importamos. ==== Importar paquetes ==== Para poder importar paquetes, tenemos que usar la cláusula **import** para poder usar su contenido. Por ejemplo, si queremos usar las clases //List// y //ArrayList//, debemos importar antes los paquetes donde están definidos: package ejemplos.T09; import java.util.ArrayList; import java.util.List; public class Librerias { public static void main(String[] args) { List numeros = new ArrayList<>(); System.out.println("Hola mundo"); } } La cláusula //import// puede utilizarse para importar un elemento concreto de un paquete (como en el ejemplo anterior) o importar todas las clases definidas en el mismo mediante *****: import java.util.*; De esta forma, tendríamos acceso a todos los elementos del paquete //java.util//. Hasta ahora hemos importado paquetes de las librerías definidas en Java, pero también podemos importar paquetes creados por nosotros mismos. Por ejemplo, crea dos paquetes llamados //paqueteA// y //paqueteB//, y dentro dos clases en cada uno: //claseA// y //claseB//: package paqueteA; import paqueteB.ClaseB; public class ClaseA { public static void main(String[] args) { ClaseB otraClase = new ClaseB(); } } package paqueteB; public class ClaseB { public ClaseB() { System.out.println("Creando la clase B"); } } Asegúrate que la visibilidad del constructor de //ClaseB// sea //public//, ya que al estar definido en otro paquete tenemos que hacerlo accesible desde el exterior. Si ejecutas la aplicación, verás como se crear el objeto de //ClaseB// y se ejecuta su constructor: Creando la clase B ===== Librerías externas ===== Hasta ahora hemos incorporado a nuestro proyecto librerías básicas de Java (//java.util//, por ejemplo). En este punto, vamos a crear nuestras propias librerías para poder incorporarlas a otros proyectos. Para eso, vamos a crear un nuevo proyecto llamado //libreriaExterna//. Cambia el nombre de la clase inicial //App// por //Calculadora// y añade el siguiente contenido: package com.fpmislata.libreriaExterna; public class Calculadora { public int sumar(int numero1, int numero2) { return numero1 + numero2; } public int restar(int numero1, int numero2) { return numero1 - numero2; } public int multiplicar(int numero1, int numero2) { return numero1 * numero2; } public int dividir(int numero1, int numero2) { return numero1 / numero2; } } Como ves, nuestro proyecto no tiene clase inicial ni método //main()//, simplemente contiene una clase llamada Calculadora que nos permite hacer las operaciones básicas sobre dos enteros (sumar, restar, multiplicar y dividir). Asegúrate de añadir el nombre del //package// al principio del archivo para después poder hacer referencia a él cuando importemos la librería. La plataforma Java ofrece la capacidad de empaquetar las aplicaciones en un único archivo que facilita su gestión. Existen diversas formas de empaquetar las aplicaciones Java, pero la más común es en forma de archivos con la extensiónn //.jar// (Java ARchive). Para crear un archivo JAR desde VSC, debemos seleccionar el segundo icono de //JAVA PROJECTS//: {{ :clase:daw:prog:2eval:libreria01.png?400 |}} Si miramos la carpeta de nuestro proyecto, veremos que nos ha creado el archivo //libreriaExterna.jar//: {{ :clase:daw:prog:2eval:libreria02.png?400 |}} Ahora ya podemos usar nuestra librería en los proyectos que queramos. Lo podemos hacer de diferentes formas. Por ejemplo, crea un nuevo proyecto. En el panel del proyecto Java, añade la referencia a nuestro archivo //.jar// mediante //Referenced Libraries//: {{ :clase:daw:prog:2eval:libreria03.png?400 |}} En la clase principal del proyecto, ya podemos usar la clase //Calculadora// de nuestra librería (tenemos que importar la librería en nuestra clase principal, obviamente): package ejemplos.T09.importar_librerias_externas; import com.fpmislata.libreriaExterna.Calculadora; public class Main { public static void main(String[] args) { Calculadora calc = new Calculadora(); System.out.println(calc.sumar(3, 4)); } } 7 También podemos guardar los archivos //.jar// en la carpeta //lib// de la raíz de nuestro proyecto. La ventaja es que al guardar una copia del archivo //.jar// no nos afectan los posibles cambios a las librerías externas (sobre todo si son de terceros). La desventaja es que nuestros proyectos pesan más, al tener incorporados más archivos. Si queremos incorporar librerías de terceros, seguimos el mismo procedimiento. Podemos bajarnos los archivos //.jar// e incorporarlos a nuestro proyecto. Otra opción es utilizar algún gestor de proyecto con gestión de dependencias como veremos a continuación. ===== Gestores de proyectos ===== Un gestor de proyecto nos ayuda a, entre otras cosas, gestionar todas las dependencias de forma automática. Imagínate que estás trabajando en un proyecto y se incorpora otra persona. Para poder empezar a trabajar en el proyecto desde su máquina, debería buscar y descargar todas las dependencias. Además, si alguna librería con la que estás trabajando se actualiza, deberías volver a descargarla para incorporar los cambios. Los gestores de dependencias te facilitan toda esa tarea. Otro problema es que, a menudo, las librerías que nos descargamos dependen de otras librerías, con lo que deberíamos descargarnos éstas también. Con un gestor de dependencias, este proceso también se hace de forma automática. En Java existen 3 gestores de proyectos que se han convertido en los más utilizados (sobre todo 2 de ellos): * **Ant**: Es el más antiguo de todos. //Ant// no requiere o impone ninguna convención de código o estructuras en el proyecto. Esto significa que //Ant// requiere que los desarrolladores escriban todos los comandos por ellos mismos, lo cual a veces nos lleva a un enorme XML (formato usado para los archivos de configuración del proyecto), que después se puede volver difícil de mantener. * **Maven**: Las limitaciones de Ant llevaron a la creación de //Maven//. //Maven// continúa usando archivos XML como //Ant// pero de una manera mucho más manejable. Mientras que //Ant// brinda flexibilidad y requiere que todo se escriba desde cero, //Maven// se basa en convenciones y proporciona comandos predefinidos (objetivos). * **Gradle**: //Gradle// es una herramienta de automatización de compilación y gestión de dependencias que se basó en los conceptos de //Ant// y //Maven//. No usa archivos XML, si no que utiliza DSL (//lenguaje específico de dominio//). Está enfocado más a construir multiproyectos. De los 3, //Maven// y //Gradle// son los más utilizados hoy en día. En nuestro caso, vamos a utilizar //Maven//, ya que la curva de aprendizaje es más sencilla, además que no vamos a hacer proyectos demasiado complejos. ==== Maven ==== //Maven// es una herramienta de gestión de proyectos de desarrollo utilizada principalmente en Java utilizando conceptos provenientes de //Ant//. Es profundamente personalizable, permitiendo finalizar tareas complejas de forma rápida y reutilizando los resultados de ejecuciones pasadas. La configuración de un proyecto se basa en un fichero XML en el cual se declaran los requerimientos para la construcción. Maven añade principalmente las siguientes funcionalidades a //Ant//: * **Gestión de repositorios**: Son ubicaciones que almacenan archivos //.jar// que serán necesarios para la construcción del proyecto. Existen tres repositorios: local, central y remoto. El primero se halla en la máquina donde se realiza el proyecto y los otros dos se accede en forma remota por //http//. Maven se centra primero en el local para la búsqueda de un JAR, si no lo encuentra buscará en remoto y lo descargará a local para acelerar futuras construcciones. * **Gestión de dependencias**: Son declaraciones de los JARS que necesita el proyecto para su construcción. * **Gestión del ciclo de vida**: Parte de unas metas y fases previamente establecidas. //Maven// trabaja con **artefactos**. Un //artefacto Maven// es como una librería Java con añadidos. Contiene las clases propias de la librería pero ademas incluye toda la información necesaria para su correcta gestión (grupo, versión, dependencias...). Al crear un proyecto de Maven, automáticamente se nos generará una estructura de carpetas muy concreta que ya viene predefinida: {{ :clase:daw:prog:2eval:maven01.png?400 |}} * **src/main/java**: Código fuente de nuestra aplicación (archivos //.java//). El contenido de este directorio se conoce bajo el nombre de módulo y lo organizaremos en paquetes. * **src/main/resources**: Recursos estáticos (XML, propiedades, imágenes…) que necesita nuestro módulo para funcionar correctamente. * **src/test/java**: Ficheros de pruebas (//testing//). * **src/test/resources**: Recursos estáticos de los ficheros de prueba. * **target**: Archivos creados en la compilación del proyecto (archivos //.class//, //.jar//...) * **pom.xml**: **Project Object Model** (POM). Es el encargado de gestionar y construir los proyectos, contiene el listado de dependencias que son necesarias para que el proyecto funcioné, plugins, etc. Toda la información del proyecto está basada en este fichero. Tiene extensión //.xml// y desde la propia web oficial de Apache Maven, lo definen como el núcleo central, por lo que podemos afirmar que es el corazón del proyecto. Lo primero que tenemos que hacer es instalar Maven en nuestro equipo. En este [[https://maven.apache.org/install.html|enlace]] tienes las instrucciones para hacerlo. Vamos a crear un proyecto //Maven// en VSC. Primero, seleccionamos //Create Java Project// desde el panel de proyectos: {{ :clase:daw:prog:2eval:maven02.png?400 |}} En //tipo de proyecto// seleccionamos //Maven//: {{ :clase:daw:prog:2eval:maven03.png?400 |}} A continuación, nos aparecen varias arquitecturas predefinidas según el tipo de proyecto que vayamos a crear (web, //React.js//, //Spring Boot//...). Seleccionamos //maven-archetype-quickstart// (proyecto //Maven// simple): {{ :clase:daw:prog:2eval:maven04.png?400 |}} Cuando creamos un proyecto, //Maven// nos pone por defecto la versión Java 1.8: UTF-8 1.8 1.8 Podemos cambiar a la versión que queramos modificando las dos líneas. A partir de Java 8, para indicar la versión podemos sustituir las 2 líneas anteriores por: UTF-8 17 Seleccionamos la versión que queramos utilizar (en nuestro caso, usaremos la última, la 1.4) y el //groupId//, que es el identificador único de la organización o grupo que crea el proyecto (en nuestro caso com.fpmislata) y el //artifactId//, que es el identificador único del artefacto principal de este proyecto (se podría decir que es el identificador del módulo dentro de la aplicación), es decir, este será el nombre del jar (escribimos ejemplo_maven). Lo siguiente será seleccionar la carpeta donde queramos crear nuestro proyecto. Una vez la seleccionemos, VSC nos preguntará la versión del proyecto y tendremos que confirmar para terminar la creación del proyecto: {{ :clase:daw:prog:2eval:maven05.png?400 |}} Si todo ha funcionado correctamente, ya tendremos nuestro proyecto //Maven// creado con la estructura de carpetas vista anteriormente (la clase principal, por defecto, se llamará //App//): {{ :clase:daw:prog:2eval:maven06.png?400 |}} === Repositorio local y remoto === Vamos a echar un vistazo al archivo //pom.xml//: {{ :clase:daw:prog:2eval:maven07.png?400 |}} Además de la configuración de la aplicación (//groupId//, //artifactId//, //version//...), tenemos una sección llamada //dependencies//: {{ :clase:daw:prog:2eval:maven08.png?400 |}} Es en esta sección donde vamos a añadir las librerías que va a utilizar nuestro proyecto. Si te fijas, ya hay una librería añadida: //junit//. Esta librería nos permite hacer pruebas (//testing//) a nuestro proyecto. Pero, ¿dónde se almacenan esas librerías? Como hemos dicho antes, //Maven// busca las librerías primero en un repositorio local. Este repositorio está situado en ///.m2/repository// (aunque Maven nos permite cambiar esta localización por defecto). Si vemos el contenido del repositorio, encontramos la librería //junit//: {{ :clase:daw:prog:2eval:maven09.png?400 |}} A la hora de buscar las dependencias de un proyecto, //Maven// mira el archivo //pom.xml// y comprueba primero si existen en el repositorio //m2// (repositorio local). En caso de que allí no existan, irá al [[https://repo.maven.apache.org/maven2/|Maven Central Repository]] (repositorio central) y las descargará en nuestro repositorio local (//m2//) para que se pueda utilizar en todos los proyectos locales. Para facilitarnos la búsqueda, podemos acceder a [[https://search.maven.org/|la página de busqueda de repositorios]] de //Maven//. Para saber más sobre repositorios locales y remotos (cambiar la ubicación, crear repositorios remotos...), podéis mirar la [[https://maven.apache.org/guides/introduction/introduction-to-repositories.html|página oficial]] de //Maven//. Si salimos a Internet a traves de un proxy, lo tendrémos que [[https://maven.apache.org/guides/mini/guide-proxies.html|configurar]] en //Maven//. === Añadiendo dependencias === Vamos a añadir una librería a nuestro proyecto para entender como funcionan los repositorios. Para añadir una dependencia, podríamos editar el archivo //pom.xml// y añadirla, pero VSC nos facilita el proceso. Si nos fijamos en el panel del proyecto Java, tenemos una sección llamada //Maven dependencies// donde podemos añadir dependencias de forma automática. Por ejemplo, vamos a añadir la librería //gson//, de google, que nos permite transformar objetos Java a Json y viceversa. Le damos a //añadir dependencia de Maven// y escribimos //gson// en el buscador: {{ :clase:daw:prog:2eval:maven10.png?400 |}} VSC nos mostrará una lista de librerías que coinciden con lo que hemos buscado. Elegimos la de //com.google.code.gson// y, si miramos ahora el archivo //pom.xml// vemos que lo ha añadido automáticamente: {{ :clase:daw:prog:2eval:maven11.png?400 |}} Cuando guardamos el archivo, VSC añadirá la dependencia a nuestro proyecto automáticamente: {{ :clase:daw:prog:2eval:maven12.png?400 |}} A partir de ahora, ya podemos utilizar la librería //Gson// en nuestros proyectos: package com.fpmislata; import com.google.gson.Gson; /** * Hello world! * */ public class App { public static void main( String[] args ) { Gson gson = new Gson(); System.out.println( "Hello World!" ); } } ¿Qué pasa si abrimos un proyecto con dependencias que no tenemos en nuestro repositorio local? Vamos a hacer la prueba. Renombra la carpeta //gson// en el directorio del repositorio local (///.m2/repository/com/google/code//) a //gson_2//: {{ :clase:daw:prog:2eval:maven13.png?400 |}} Ahora reinicia VSC y vuelve a mirar la carpeta anterior: {{ :clase:daw:prog:2eval:maven14.png?400 |}} Como ves, //Maven// ha descargado de forma automática la librería desde el repositorio central, ya que no la ha encontrado en el repositorio local. Ésto es muy útil, ya que para empezar a trabajar con un proyecto que contiene librerías externas, solo tenemos que abrirlo y el IDE se encargará de descargar las dependencias necesarias. Eso sí, puede que al abrir proyectos a partir de ahora el IDE tarde un poco más (sobre todo al principio) al necesitar comprobar y descargar las dependencias que no están en el repositorio local. ===== 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).