<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Del código a producción on juanfbermejo.dev</title><link>/series/del-codigo-a-produccion/</link><description>Recent content in Del código a producción on juanfbermejo.dev</description><image><title>juanfbermejo.dev</title><url>/favicon-32x32.png</url><link>/favicon-32x32.png</link></image><generator>Hugo -- 0.152.2</generator><language>es</language><lastBuildDate>Sun, 01 Mar 2026 08:06:08 +0100</lastBuildDate><atom:link href="/series/del-codigo-a-produccion/index.xml" rel="self" type="application/rss+xml"/><item><title>Compilar con Maven dentro de Docker - multi-stage build</title><link>/posts/compilar-app-maven-docker/</link><pubDate>Sun, 01 Mar 2026 08:06:08 +0100</pubDate><guid>/posts/compilar-app-maven-docker/</guid><description>&lt;p&gt;Una vez que hemos desacoplado la ejecución de una aplicación de nuestro equipo y su entorno desplegándola en un contenedor, el siguiente paso es &lt;strong&gt;independizar también el proceso de compilación y construcción&lt;/strong&gt;. El objetivo es evitar también problemas de configuraciones y dependencias relacionadas con el entorno de desarrollo, &lt;strong&gt;moviendo la responsabilidad a un proceso automatizado&lt;/strong&gt;. De esta forma nos acercarnos más a un entorno de producción.&lt;/p&gt;
&lt;h1 id="enfoque-de-partida"&gt;Enfoque de partida&lt;/h1&gt;
&lt;p&gt;El Dockerfile con el que hemos trabajado previamente incluye lo mínimo para desplegar el JAR:&lt;/p&gt;</description><content:encoded><![CDATA[<p>Una vez que hemos desacoplado la ejecución de una aplicación de nuestro equipo y su entorno desplegándola en un contenedor, el siguiente paso es <strong>independizar también el proceso de compilación y construcción</strong>. El objetivo es evitar también problemas de configuraciones y dependencias relacionadas con el entorno de desarrollo, <strong>moviendo la responsabilidad a un proceso automatizado</strong>. De esta forma nos acercarnos más a un entorno de producción.</p>
<h1 id="enfoque-de-partida">Enfoque de partida</h1>
<p>El Dockerfile con el que hemos trabajado previamente incluye lo mínimo para desplegar el JAR:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Dockerfile" data-lang="Dockerfile"><span class="line"><span class="cl"><span class="k">FROM</span><span class="w"> </span><span class="s">eclipse-temurin:17-jre-alpine</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">WORKDIR</span><span class="w"> </span><span class="s">/app</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">COPY</span> target/app.jar app.jar<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">ENTRYPOINT</span> <span class="p">[</span><span class="s2">&#34;java&#34;</span><span class="p">,</span><span class="s2">&#34;-jar&#34;</span><span class="p">,</span><span class="s2">&#34;app.jar&#34;</span><span class="p">]</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Los problemas que presenta son:</p>
<ul>
<li>Necesitamos compilar la aplicación fuera del contenedor.</li>
<li>Dependencias de Maven instaladas en el entorno de desarrollo.</li>
<li>No es reproducible al 100%.</li>
<li>El proceso de compilación y despliegue depende de varios pasos manuales.</li>
</ul>
<p>Para completar el proceso, podríamos incorporar en el mismo contenedor lo necesario para realizar la compilación, pero <strong>todas las herramientas necesarias para ello convivirían con la aplicación desplegada en el contenedor de producción</strong>.</p>
<h1 id="qué-es-un-multi-stage-build">Qué es un multi-stage build</h1>
<p>Un multi-stage build es una técnica de Docker que consiste en <strong>usar un contenedor para construir la aplicación y otro distinto, mucho más pequeño, solo para ejecutarla</strong>. Así Docker, compila dentro de un contenedor pesado con las dependencias necesarias para ese proceso, descarta todo lo innecesario y se queda con la aplicación final compilada para desplegarla en otro contenedor mucho más ligero.</p>
<h2 id="esquema-del-proceso">Esquema del proceso</h2>
<ol>
<li>Stage 1 (builder)
<ul>
<li>Tiene Maven</li>
<li>Compila el proyecto</li>
<li>Genera el JAR</li>
</ul>
</li>
<li>Stage 2 (runtime)
<ul>
<li>Solo tiene Java</li>
<li>Copia el JAR</li>
<li>Ejecuta la app</li>
</ul>
</li>
</ol>
<p>De esta forma, <strong>el contenedor final no contiene Maven, ni código fuente, ni caché</strong>.</p>
<h1 id="nuevo-dockerfile-multi-stage">Nuevo Dockerfile multi-stage</h1>
<p>El nuevo Dockerfile completo con las dos etapas quedaría así:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Dockerfile" data-lang="Dockerfile"><span class="line"><span class="cl"><span class="c"># ---------- STAGE 1: build ----------</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">FROM</span><span class="w"> </span><span class="s">maven:3.9.9-eclipse-temurin-21-alpine</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">build</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">WORKDIR</span><span class="w"> </span><span class="s">/app</span> <span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">COPY</span> pom.xml .<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">RUN</span> mvn dependency:go-offline<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">COPY</span> src ./src<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">RUN</span> mvn clean package -DskipTests<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># ---------- STAGE 2: runtime ----------</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">FROM</span><span class="w"> </span><span class="s">eclipse-temurin:21-jre-alpine-3.23</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">WORKDIR</span><span class="w"> </span><span class="s">/app</span> <span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">COPY</span> --from<span class="o">=</span>build /app/target/*-SNAPSHOT.jar app.jar<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">EXPOSE</span><span class="w"> </span><span class="s">8080</span> <span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">ENTRYPOINT</span> <span class="p">[</span><span class="s2">&#34;java&#34;</span><span class="p">,</span><span class="s2">&#34;-jar&#34;</span><span class="p">,</span><span class="s2">&#34;app.jar&#34;</span><span class="p">]</span></span></span></code></pre></td></tr></table>
</div>
</div>
<h2 id="stage-1---build-con-maven">Stage 1 - Build con Maven</h2>
<p><code>FROM maven:3.9.9-eclipse-temurin-21-alpine AS build</code></p>
<ul>
<li>Imagen base con Maven + JDK 21.</li>
<li><code>AS build</code>: le ponemos nombre a esta fase para poder referenciarla después.</li>
</ul>
<p><code>WORKDIR /app</code></p>
<ul>
<li>Establece el directorio de trabajo dentro del contenedor.</li>
<li>A partir de aquí, todos los comandos se ejecutan desde <code>/app</code>. (equivalente a hacer <code>cd /app</code>)</li>
</ul>
<p><code>COPY pom.xml .</code></p>
<ul>
<li>Copia únicamente el <code>pom.xml</code> al contenedor.</li>
<li>Se hace así para aprovechar la caché de Docker y no tener que descargar dependencias cada vez que cambia el código fuente.</li>
</ul>
<p><code>RUN mvn dependency:go-offline</code></p>
<ul>
<li>Descarga todas las dependencias declaradas en el <code>pom.xml</code>.</li>
<li>Si después cambia el código pero no las dependencias, Docker reutiliza esta capa y el build es mucho más rápido.</li>
<li><code>go-offline</code> es un goal del plugin de Maven, indica que se deben descargar todas las dependencias necesarias para que el proyecto pueda compilarse sin conexión a internet. Es útil para mantener las descargas en la caché de Docker.</li>
</ul>
<p><code>COPY src ./src</code></p>
<ul>
<li>Copia el código fuente de la aplicación dentro del contenedor.</li>
<li>Ya tenemos todo lo necesario para compilar.</li>
</ul>
<p><code>RUN mvn clean package -DskipTests</code></p>
<ul>
<li><code>clean</code> elimina compilaciones anteriores.</li>
<li><code>package</code> compila y genera el JAR ejecutable.</li>
<li><code>-DskipTests</code> evita ejecutar los tests durante la construcción de la imagen. En entornos reales, los tests suelen ejecutarse previamente en un pipeline de integración continua. Separar la fase de validación de la fase de construcción permite builds más rápidos y predecibles.</li>
<li>Aquí se genera el mismo JAR que antes creábamos en local, pero ahora dentro del contenedor.</li>
</ul>
<h2 id="stage-2---runtime-limpio">Stage 2 - Runtime limpio</h2>
<p>Comienza una nueva fase completamente independiente.</p>
<p><code>FROM eclipse-temurin:21-jre-alpine-3.23</code></p>
<ul>
<li>Imagen ligera con solo el entorno de ejecución Java.</li>
<li>No incluye Maven ni herramientas de desarrollo.</li>
</ul>
<p><code>WORKDIR /app </code></p>
<ul>
<li>De nuevo, definimos el directorio de trabajo.</li>
<li>Cada <code>FROM</code> reinicia el contexto, por eso hay que volver a declararlo.</li>
</ul>
<p><code>COPY --from=build /app/target/*-SNAPSHOT.jar app.jar</code></p>
<ul>
<li><code>--from=build</code> indica que copiamos desde el stage anterior.</li>
<li>Copiamos el JAR generado dentro del contenedor de build.</li>
</ul>
<p><code>EXPOSE 8080</code></p>
<ul>
<li>Indica que la aplicación escucha en el puerto 8080.</li>
<li>No abre el puerto, solo lo documenta a nivel de imagen.</li>
</ul>
<p><code>ENTRYPOINT [&quot;java&quot;,&quot;-jar&quot;,&quot;app.jar&quot;]</code></p>
<ul>
<li>Define el comando que se ejecutará cuando el contenedor arranque.</li>
<li>Lanza la aplicación Spring Boot.</li>
<li>Es equivalente a ejecutar <code>java -jar app.jar</code></li>
</ul>
<p>Con esto, <strong>hemos separado el proceso de construcción del proceso de ejecución</strong>. El contenedor ya no depende del entorno del desarrollador, sino de un proceso reproducible y aislado. <strong>Igual que separamos responsabilidades en el código, aquí separamos responsabilidades en el contenedor</strong>.</p>
<h1 id="construcción-de-los-contenedores">Construcción de los contenedores</h1>
<p>De forma similar a cómo construíamos la imagen anterior, lanzamos el siguiente comando en consola que, ahora realizará la compilación de la aplicación y generará la imagen para desplegarla (etiquetada como version 2.0).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker build -t hello-world-api:2.0 .</span></span></code></pre></div>
<p>En este caso el tiempo para finalizar será superior, aunque será más evidente solo en la primera ejecución. Si el proceso finaliza correctamente, tendremos la nueva imagen disponible.</p>
<p><img alt="Resultado en terminal tras lanzar la construcción de la imagen del contenedor" loading="lazy" src="/posts/compilar-app-maven-docker/images/compilando-app-docker.png"></p>
<p>Para desplegar el contenedor con la aplicación, ejecutamos el siguiente comando:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker run -p 8080:8080 hello-world-api:2.0</span></span></code></pre></div>
<p>De esta forma, tendremos la aplicación compilada y desplegada, sin haber ejecutado Maven en nuestro equipo.</p>
<p><img alt="Hello World ejecutado en el navegador desde el contenedor final" loading="lazy" src="/posts/compilar-app-maven-docker/images/hello-world-from-docker.png"></p>
<h1 id="comparativa-con-el-enfoque-anterior">Comparativa con el enfoque anterior</h1>
<p>Hemos conseguido desacoplar la compilación del entorno del desarrollador y mover esa responsabilidad al contenedor. Pero además usando multi-stage conseguimos que esa imagen final:</p>
<ul>
<li>No incluye herramientas de construcción innecesarias.</li>
<li>Mantiene una imagen final ligera.</li>
<li>Evita inflar la imagen con Maven y código fuente.</li>
<li>Reproducible totalmente en otros entornos.</li>
<li>Lista para CI/CD.</li>
<li>Más segura (al incluir solo los elementos mínimos para su ejecución).</li>
</ul>
<h1 id="reflexión">Reflexión</h1>
<p>Existe una premisa fundamental a la hora de trabajar con contenedores: <strong>Una imagen debe contener solo lo necesario para ejecutarse. Nada más.</strong> Debido a ello, multi-stage es el <strong>planteamiento recomendado en entornos reales</strong>, salvo para la construcción de prototipos ultra rápidos.</p>
<p>Por otro lado, desacoplando la compilación del entorno del desarrollador ya no importa que versión de Maven tiene, si está instalado, si otro desarrollador usa otro sistema operativo, si el servidor se actualiza, etc. <strong>El contenedor se convierte en el entorno estándar de construcción de la aplicación</strong>. <strong>Esto es el enfoque DevOps</strong>.</p>
]]></content:encoded></item><item><title>Primer test en Spring Boot</title><link>/posts/primer-test-spring-boot/</link><pubDate>Fri, 06 Feb 2026 16:23:02 +0100</pubDate><guid>/posts/primer-test-spring-boot/</guid><description>&lt;p&gt;Alrededor de la programación hay una serie de tareas menos agradecidas y poco atractivas para la mayoría de los desarrolladores: &lt;strong&gt;el análisis, la documentación… los tests y las pruebas&lt;/strong&gt;. Con cada nuevo evolutivo solemos lanzarnos directamente al teclado, abrir nuestro IDE y empezar a &lt;em&gt;picar código&lt;/em&gt;, cuando muchas veces &lt;strong&gt;lo más efectivo a largo plazo es comenzar con papel y bolígrafo, desgranando qué queremos hacer realmente&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Con las pruebas y los tests sucede algo similar. Durante el desarrollo vamos lanzando comprobaciones manuales y, cuando vemos que todo más o menos funciona, lo damos por válido. Puede que incluso documentemos alguna de ellas, pero suele percibirse como otra tarea pesada que no siempre motiva y que intentamos quitarnos de encima cuanto antes.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Alrededor de la programación hay una serie de tareas menos agradecidas y poco atractivas para la mayoría de los desarrolladores: <strong>el análisis, la documentación… los tests y las pruebas</strong>. Con cada nuevo evolutivo solemos lanzarnos directamente al teclado, abrir nuestro IDE  y empezar a <em>picar código</em>, cuando muchas veces <strong>lo más efectivo a largo plazo es comenzar con papel y bolígrafo, desgranando qué queremos hacer realmente</strong>.</p>
<p>Con las pruebas y los tests sucede algo similar. Durante el desarrollo vamos lanzando comprobaciones manuales y, cuando vemos que todo más o menos funciona, lo damos por válido. Puede que incluso documentemos alguna de ellas, pero suele percibirse como otra tarea pesada que no siempre motiva y que intentamos quitarnos de encima cuanto antes.</p>
<p>Sin embargo, al igual que un buen análisis, diseño y planificación nos ahorra tiempo y evita errores a largo plazo, <strong>un buen diseño de los casos de prueba y su automatización contribuye a obtener un mejor resultado final</strong>. Además, nos garantiza que podremos detectar rápidamente si un nuevo evolutivo introduce errores que no habíamos contemplado.</p>
<h1 id="testing-en-spring-boot">Testing en Spring Boot</h1>
<p>Spring Boot incorpora <em>de serie</em> varias herramientas que facilitan enormemente la implementación de los primeros tests. Entre ellas destacan:</p>
<ul>
<li><strong>JUnit 5</strong>: es un framework de testing que nos permite definir y ejecutar test en Java mediante anotaciones como <code>@Test</code>.</li>
<li><strong>Spring Boot Test</strong>: conjunto de utilidades que facilita probar aplicaciones Spring cargando el contexto, inyectando dependencias y simulando peticiones HTTP.</li>
<li><strong>Maven configurado para ejecutar test</strong>: Maven ejecuta automáticamente los test durante el ciclo de compilación, fallando el proceso si alguno no pasa y evitando la generación de artefactos incorrectos.</li>
</ul>
<h1 id="primer-test-de-hello-world">Primer test de Hello World</h1>
<p>Para implementar los primeros tests nos vamos a basar en el <a href="/posts/spring-boot-hello-world/"><em>Hello World</em> que desarrollamos en el artículo anterior</a> y que <a href="/posts/compilar-app-maven/">ya hemos compilado con Maven</a>.</p>
<p>Al crear la estructura básica del proyecto con Spring Initializr e incluir la dependencia <em>Spring Web</em>, ya deberíamos tener en el <code>pom.xml</code> la dependencia <code>spring-boot-starter-webmvc-test</code>, que nos proporciona todo lo necesario para estos primeros tests.</p>
<h2 id="la-aplicación-arranca">La aplicación arranca</h2>
<p>Se trata de un test de contexto, anotado con <code>@SpringBootTest</code>, que ya tendremos creado automáticamente en la estructura del proyecto dentro de la carpeta <code>src/test</code>. En nuestro caso se denomina <code>SpringBootHelloWorldApplicationTests</code>.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nn">dev.juanfbermejo.SpringBootHelloWorld</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.junit.jupiter.api.Test</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.springframework.boot.test.context.SpringBootTest</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nd">@SpringBootTest</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">class</span> <span class="nc">SpringBootHelloWorldApplicationTests</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nd">@Test</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kt">void</span><span class="w"> </span><span class="nf">contextLoads</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Este test no contiene lógica alguna, pero precisamente ahí reside su valor. Comprueba que el contexto de Spring arranca correctamente y, si falla, indica que existe un problema grave en la configuración de la aplicación.</p>
<h2 id="testear-un-endpoint-sencillo-hello-world">Testear un endpoint sencillo: Hello World</h2>
<p>El siguiente paso es diseñar e implementar un test para nuestro endpoint <code>/hello</code>. Aunque se trata de un endpoint muy sencillo, ya podemos validar tres aspectos básicos:</p>
<ol>
<li>Que el endpoint existe bajo la URL</li>
<li>Que responde con código HTTP 200</li>
<li>Que devuelve el contenido esperado</li>
</ol>
<p>El código del test es el siguiente:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nn">dev.juanfbermejo.SpringBootHelloWorld</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.junit.jupiter.api.Test</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.springframework.beans.factory.annotation.Autowired</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.springframework.boot.test.context.SpringBootTest</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.springframework.test.web.servlet.MockMvc</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import static</span><span class="w"> </span><span class="nn">org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import static</span><span class="w"> </span><span class="nn">org.springframework.test.web.servlet.result.MockMvcResultMatchers.content</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import static</span><span class="w"> </span><span class="nn">org.springframework.test.web.servlet.result.MockMvcResultMatchers.status</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nd">@SpringBootTest</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nd">@AutoConfigureMockMvc</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">HelloControllerTest</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nd">@Autowired</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="n">MockMvc</span><span class="w"> </span><span class="n">mockMvc</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nd">@Test</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kt">void</span><span class="w"> </span><span class="nf">helloEndpointReturnsHelloWorld</span><span class="p">()</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">Exception</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">mockMvc</span><span class="p">.</span><span class="na">perform</span><span class="p">(</span><span class="w"> </span><span class="n">get</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="w"> </span><span class="n">status</span><span class="p">().</span><span class="na">isOk</span><span class="p">()</span><span class="w"> </span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="w"> </span><span class="n">content</span><span class="p">().</span><span class="na">string</span><span class="p">(</span><span class="s">&#34;Hello World from Spring Boot!&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<h3 id="elementos-que-se-usan-en-el-test">Elementos que se usan en el test</h3>
<h4 id="anotaciones-de-spring-y-junit">Anotaciones de Spring y JUnit:</h4>
<ul>
<li><code>@Autowired</code>: le indica a Spring que inyecte automáticamente una instancia de <code>MockMvc</code> en el test.</li>
<li><code>@Test</code>: anotación de JUnit 5 que marca un método como test y permite que el framework lo ejecute durante la fase de testing.</li>
<li><code>@AutoConfigureMockMvc</code>: indica a Spring Boot que configure automáticamente <code>MockMvc</code>, permitiéndonos simular peticiones HTTP a los endpoints sin arrancar un servidor real.</li>
</ul>
<h4 id="herramientas-para-simular-peticiones-http">Herramientas para simular peticiones HTTP:</h4>
<ul>
<li><code>MockMvc</code>: utilidad de Spring para simular llamadas HTTP (GET, POST, etc.) contra los controladores y verificar sus respuestas.</li>
<li><code>get()</code>: construye una petición HTTP de tipo GET contra la ruta indicada, en nuestro ejemplo <code>/hello</code>.</li>
<li><code>perform()</code>: ejecuta la petición HTTP simulada y definida previamente. Devuelve el resultado para poder hacer comprobaciones sobre la respuesta.</li>
</ul>
<h4 id="validación-de-la-respuesta">Validación de la respuesta:</h4>
<ul>
<li><code>andExpect()</code>: define una expectativa sobre la respuesta obtenida; si no se cumple, el test falla.</li>
<li><code>status()</code> e <code>isOk()</code>: comprueban que el código de estado HTTP de la respuesta sea 200 (OK).</li>
<li><code>content()</code>: permite validar el contenido del cuerpo de la respuesta.</li>
</ul>
<h1 id="ejecutar-los-test-con-maven">Ejecutar los test con Maven</h1>
<p>Una vez configurados los tests, podemos ejecutarlos desde el propio IDE o, al igual que para la construcción del artefacto, desde la línea de comandos utilizando Maven.</p>
<p>Desde un terminal, situados en la carpeta raíz del proyecto, ejecutamos:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mvn test</span></span></code></pre></div>
<p>Maven compilará el código y lanzará todos los tests definidos en el proyecto, mostrando el resultado en la consola.</p>
<p><img alt="Terminal que muestra que el test ha dado resultado positivo" loading="lazy" src="/posts/primer-test-spring-boot/images/mvn_test_ok.png"></p>
<p>Si forzamos un error -por ejemplo, cambiando el texto esperado en el cuerpo de la respuesta- y ejecutamos de nuevo el comando, veremos cómo el test falla. La salida por consola indicará qué test ha fallado, qué se esperaba que ocurriera y cuál ha sido el resultado real, proporcionando información muy útil para entender el problema y corregirlo.</p>
<p><img alt="Terminal que muestra que el test ha dado resultado negativo" loading="lazy" src="/posts/primer-test-spring-boot/images/mvn_test_ko.png"></p>
<h1 id="conclusiones">Conclusiones</h1>
<p>En este artículo hemos dado el primer paso en la incorporación de tests automatizados a nuestra API Spring Boot. Partiendo de un ejemplo muy sencillo, hemos visto cómo <strong>validar que la aplicación arranca correctamente y cómo comprobar el comportamiento de un endpoint HTTP mediante tests automatizados</strong>.</p>
<p>Hasta ahora, en la serie hemos creado una API básica, la hemos compilado con Maven y la hemos preparado para su ejecución. Con estos primeros tests añadimos <strong>una capa fundamental de seguridad que nos permitirá evolucionar el código con mayor confianza</strong>.</p>
<p>En los siguientes artículos iremos ampliando este enfoque, incorporando tests más completos y sentando las bases para su integración en un proceso de integración continua (CI), donde estos tests se ejecutarán automáticamente en cada cambio del código.</p>
]]></content:encoded></item><item><title>Desplegar una aplicación Spring Boot en un contenedor</title><link>/posts/desplegar-aplicacion-contenedor/</link><pubDate>Fri, 30 Jan 2026 17:11:15 +0100</pubDate><guid>/posts/desplegar-aplicacion-contenedor/</guid><description>&lt;p&gt;Hasta ahora, &lt;a href="/posts/spring-boot-hello-world/"&gt;hemos creado una aplicación Hello World con Spring Boot&lt;/a&gt;, la hemos &lt;a href="/posts/compilar-app-maven/"&gt;compilado con Maven y la hemos ejecutado correctamente en nuestro equipo&lt;/a&gt;. Sin embargo, &lt;strong&gt;ejecutar una aplicación en local es solo el primer paso&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;En cuanto queremos compartirla, desplegarla en un servidor o moverla entre distintos entornos (desarrollo, pruebas, producción), empiezan a aparecer los problemas clásicos: &lt;strong&gt;versiones diferentes de Java, dependencias que no coinciden, configuraciones específicas de la máquina o incluso sistemas operativos diferentes&lt;/strong&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Hasta ahora, <a href="/posts/spring-boot-hello-world/">hemos creado una aplicación Hello World con Spring Boot</a>, la hemos <a href="/posts/compilar-app-maven/">compilado con Maven y la hemos ejecutado correctamente en nuestro equipo</a>. Sin embargo, <strong>ejecutar una aplicación en local es solo el primer paso</strong>.</p>
<p>En cuanto queremos compartirla, desplegarla en un servidor o moverla entre distintos entornos (desarrollo, pruebas, producción), empiezan a aparecer los problemas clásicos: <strong>versiones diferentes de Java, dependencias que no coinciden, configuraciones específicas de la máquina o incluso sistemas operativos diferentes</strong>.</p>
<p>Aquí es donde <strong>encapsular la aplicación en un contenedor</strong> se convierte en el siguiente paso natural.</p>
<h1 id="qué-es-un-contenedor">Qué es un contenedor</h1>
<p>Un contenedor <strong>nos permite empaquetar la aplicación junto con todo lo que necesita para ejecutarse</strong>: la versión de Java, las dependencias y la configuración básica del entorno. El resultado es un <strong>paquete autocontenido que se comporta igual en cualquier máquina donde se ejecute</strong>.</p>
<p><a href="/posts/instalar-docker-windows/">Docker</a> es una de las herramientas más extendidas para crear, ejecutar y gestionar contenedores de forma sencilla.</p>
<h1 id="configuración-del-contenedor">Configuración del contenedor</h1>
<p>De forma similar al fichero <code>pom.xml</code> de Maven, para configurar un contenedor Docker usaremos un fichero <code>Dockerfile</code> (sin extensión) que se debe ubicar en la misma ruta del proyecto.</p>
<p>Con el siguiente Dockerfile mínimo se está creando de la forma más simple y limpia un contenedor Docker para ejecutar una aplicación Spring Boot:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="k">FROM</span><span class="w"> </span><span class="s">eclipse-temurin:21-jre-alpine-3.23</span>  <span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">WORKDIR</span><span class="w"> </span><span class="s">/app</span>  <span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">COPY</span> target/SpringBootHelloWorld-0.0.1-SNAPSHOT.jar app.jar  <span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">EXPOSE</span><span class="w"> </span><span class="s">8080</span>  <span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">ENTRYPOINT</span> <span class="p">[</span><span class="s2">&#34;java&#34;</span><span class="p">,</span><span class="s2">&#34;-jar&#34;</span><span class="p">,</span><span class="s2">&#34;app.jar&#34;</span><span class="p">]</span></span></span></code></pre></td></tr></table>
</div>
</div>
<ul>
<li>En primer lugar se indica una imagen base para el contenedor. En este caso se trata de una distribución ligera de Linux con Java 21 (solo el entorno de ejecución), suficiente para ejecutar una aplicación Spring Boot.</li>
<li>Después se indica el directorio de trabajo dentro del contenedor. A partir de aquí, todos los comandos se ejecutarán desde /app.</li>
<li>Se copia el JAR generado por Maven desde el equipo anfitrión al contenedor, renombrándolo como app.jar. El nombre del JAR debe coincidir exactamente con el generado.</li>
<li>Se indica el puerto en el que escucha la aplicación, que en Spring Boot suele ser el 8080 por defecto, pero a nivel informativo, no se abre el puerto.</li>
<li>Por último, se define el comando que se ejecutará al arrancar el contenedor, lanzando la aplicación Spring Boot.</li>
</ul>
<h1 id="construcción-de-la-imagen">Construcción de la imagen</h1>
<p>El primer paso es construir una imagen Docker a partir de la cual se podrán crear instancias del contenedor y desplegarlas.</p>
<p>Con Docker ejecutándose, y desde la raíz del proyecto, se lanza el siguiente comando:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker build -t hello-world-api:1.0 .</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Qué significa:</p>
<ul>
<li><code>-t helloWorld-api:1.0</code> es el nombre y versión de la imagen.</li>
<li><code>.</code> indica que se trabaja en el directorio actual, donde está el Dockerfile sobre el que generar la imagen.</li>
</ul>
<p><img alt="Pantalla de terminal que muestra el resultado de la construcción de la imagen Docker" loading="lazy" src="/posts/desplegar-aplicacion-contenedor/images/docker_build.png"></p>
<p>Si todo va bien, podemos listar las imágenes del sistema con el comando <code>docker images</code>.</p>
<p><img alt="Pantalla de terminal que muestra el listado de imágenes disponibles" loading="lazy" src="/posts/desplegar-aplicacion-contenedor/images/docker_images.png"></p>
<h1 id="ejecutar-el-contenedor">Ejecutar el contenedor</h1>
<p>Para crear y ejecutar un contenedor a partir de la imagen generada, se lanza el siguiente comando:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker run -p 8080:8080 --name hello-world-api-container hello-world-api:1.0</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Qué significa:</p>
<ul>
<li><code>-p 8080:8080</code> indica el puerto del equipo donde se quieren recibir las peticiones del contenedor, seguido del puerto del contenedor a donde deben dirigirse. En este caso, usa el mismo.</li>
<li><code>--name helloWorld-api-container</code> indica un nombre para identificar al contenedor.</li>
</ul>
<p><img alt="Pantalla de terminal que muestra el contenedor construido en ejecución" loading="lazy" src="/posts/desplegar-aplicacion-contenedor/images/docker_run.png"></p>
<p>De nuevo, si todo va bien, podremos abrir un navegador y acceder a la url <code>http://localhost:8080</code> y ver el mensaje de &lsquo;Hello World&rsquo;, esta vez lanzado desde la API en el contenedor.</p>
<p><img alt="Pantalla del navegador con la invocación a la API Hello World desplegada en el contenedor" loading="lazy" src="/posts/desplegar-aplicacion-contenedor/images/docker_hello.png"></p>
<p>Para parar el contenedor, que se habrá quedado en la terminal, pulsamos la combinación de teclas <code>Ctrl + C</code>.</p>
<h1 id="otros-comandos-útiles">Otros comandos útiles</h1>
<h2 id="ejecución-del-contenedor-en-segundo-plano">Ejecución del contenedor en segundo plano</h2>
<p>Para no tener que mantener la ventana de terminal con el contenedor en ejecución, se puede lanzar en segundo plano añadiendo <code>-d</code> al comando <code>run</code> de la siguiente manera:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker run -d -p 8080:8080 --name helloWorld-api-container helloWorld-api:1.0</span></span></code></pre></td></tr></table>
</div>
</div>
<h2 id="ver-logs-del-contenedor">Ver logs del contenedor</h2>
<p>Para consultar los logs en la consola de Spring Boot dentro del contenedor, lanzamos el siguiente comando:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker logs helloWorld-api-container</span></span></code></pre></td></tr></table>
</div>
</div>
<h2 id="detener-el-contenedor">Detener el contenedor</h2>
<p>Al no estar ahora ligada la ejecución del contenedor a la sesión de terminal, para detenerlo, se debe lanzar el siguiente comando:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker stop helloWorld-api-container</span></span></code></pre></td></tr></table>
</div>
</div>
<h1 id="conclusiones">Conclusiones</h1>
<p>Ya hemos dado un paso clave: <strong>llevar nuestra aplicación Spring Boot desde un simple JAR hasta un contenedor ejecutable</strong>, garantizando que se comporta igual independientemente del entorno donde se despliegue.</p>
<p>El flujo que hemos seguido hasta ahora es muy sencillo, pero extremadamente importante:</p>
<ul>
<li>Hemos desarrollado una <strong>API sencilla en Spring Boot</strong></li>
<li>Con <strong>Apache Maven</strong> hemos generado un artefacto ejecutable (el JAR)</li>
<li>Ese JAR lo hemos encapsulado en un contenedor usando <strong>Docker</strong>, resolviendo de un plumazo los problemas de dependencias, versiones y entornos.</li>
<li>Hemos visto que desplegar la aplicación se reduce a construir una imagen y arrancar un contenedor.</li>
</ul>
<p>A partir de aquí, se abre un abanico de mejoras naturales como optimizar el proceso, orquestar varios servicios y llegar a automatizar todo el flujo de construcción y despliegue. Todo ello de forma cercana a un entorno real de producción.</p>
]]></content:encoded></item><item><title>Pasos para instalar Docker en Windows</title><link>/posts/instalar-docker-windows/</link><pubDate>Sun, 25 Jan 2026 19:54:26 +0100</pubDate><guid>/posts/instalar-docker-windows/</guid><description>&lt;h1 id="qué-es-docker"&gt;¿Qué es Docker?&lt;/h1&gt;
&lt;p&gt;Docker permite &lt;strong&gt;crear y ejecutar aplicaciones dentro de contenedores&lt;/strong&gt;, facilitando un &lt;strong&gt;entorno consistente y reproducible tanto en desarrollo y despliegue&lt;/strong&gt;. Esto evita el clásico &lt;em&gt;“en mi máquina funciona”&lt;/em&gt; y lo convierte en una herramienta clave en flujos modernos de CI/CD.&lt;/p&gt;
&lt;h1 id="instalación-de-docker-en-windows"&gt;Instalación de Docker en Windows&lt;/h1&gt;
&lt;p&gt;En Windows, la forma recomendada de trabajar con Docker es mediante &lt;a href="https://www.docker.com/products/docker-desktop/"&gt;Docker Desktop&lt;/a&gt;, ya que su instalación es muy sencilla e incluye todo lo necesario para empezar.&lt;/p&gt;</description><content:encoded><![CDATA[<h1 id="qué-es-docker">¿Qué es Docker?</h1>
<p>Docker permite <strong>crear y ejecutar aplicaciones dentro de contenedores</strong>, facilitando un <strong>entorno consistente y reproducible tanto en desarrollo y despliegue</strong>. Esto evita el clásico <em>“en mi máquina funciona”</em> y lo convierte en una herramienta clave en flujos modernos de CI/CD.</p>
<h1 id="instalación-de-docker-en-windows">Instalación de Docker en Windows</h1>
<p>En Windows, la forma recomendada de trabajar con Docker es mediante <a href="https://www.docker.com/products/docker-desktop/">Docker Desktop</a>, ya que su instalación es muy sencilla e incluye todo lo necesario para empezar.</p>
<h2 id="requisitos-previos">Requisitos previos</h2>
<p>En la actualidad, para poder instalar Docker en Windows, el sistema debe cumplir los siguientes requisitos:</p>
<ul>
<li>Contar con Windows 10 u 11 de 64 bits (Home o Pro)</li>
<li>Tener habilitada la virtualización en la BIOS</li>
<li>Tener activo WSL 2 (Windows Subsystem for Linux)</li>
</ul>
<h1 id="virtualización-habilitada">Virtualización habilitada</h1>
<p>Para poder ejecutar Docker y contenedores en un equipo, no solo debe contar con un procesador que lo soporte, también debe tener habilitada la opción de virtualización en la BIOS.</p>
<p>Para verificarlo, se puede acudir al Administrador de tareas de Windows, sección CPU, donde en la parte inferior aparece una etiqueta que indica si la virtualización está habilitada.</p>
<p><img alt="Administrador de tareas de Windows donde se muestra si está habilitada la virtualización" loading="lazy" src="/posts/instalar-docker-windows/images/virtualizacion_habilitada.png"></p>
<p>Si la configuración indica que está deshabilitada, se debe cambiar desde la BIOS del propio equipo siguiendo las indicaciones específicas del fabricante.</p>
<h1 id="instalación-de-wsl-2">Instalación de WSL 2</h1>
<p>WSL (Windows Subsystem for Linux) permite <strong>ejecutar un entorno Linux directamente en Windows</strong>. Su versión más moderna, <strong>WSL 2</strong>, ofrece mejor rendimiento y compatibilidad, y es la opción recomendada para trabajar con Docker. Docker Desktop utiliza WSL 2 como backend para ejecutar los contenedores Linux de forma eficiente dentro de Windows.</p>
<p>La instalación es muy sencilla, solo habrá que lanzar el comando siguiente en una terminal.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wsl --install</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Esto, automáticamente:</p>
<ul>
<li>Habilita las características necesarias de Windows.</li>
<li>Instala <strong>WSL 2</strong> (la versión recomendada).</li>
<li>Descarga e instala <strong>Ubuntu</strong> como distribución por defecto.</li>
</ul>
<p>Tras un reinicio, nos pedirá crear un usuario de Linux con una contraseña (no es necesario que coincida con los de Windows.)</p>
<p>Para usar Linux desde Windows, basta con abrir una terminal y lanzar el siguiente comando.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wsl</span></span></code></pre></td></tr></table>
</div>
</div>
<h1 id="descarga-e-instalación-de-docker-desktop">Descarga e instalación de Docker Desktop</h1>
<p>Docker Desktop para Windows está disponible para su descarga desde la <a href="https://www.docker.com/products/docker-desktop/">web oficial de Docker</a>. Ahí está disponible un instalador ejecutable.</p>
<p>Una vez descargado, basta con ejecutarlo siguiendo los pasos marcados, y marcando la opción de &ldquo;Use WSL 2 instead of Hyper-V&rdquo; cuando nos lo solicite.</p>
<p>Al finalizar, es posible que sea necesario reiniciar el equipo. Tras ello, ejecutamos Docker Desktop desde el menú Inicio. La primera vez puede tardar unos minutos mientras se configuran los componentes necesarios, llegando a una pantalla similar a la siguiente si todo ha ido bien.</p>
<p><img alt="Ventana de Docker Desktop en ejecución" loading="lazy" src="/posts/instalar-docker-windows/images/docker_desktop.png"></p>
<p>También se puede verificar que todo está funcionando desde la terminal, lanzado el siguiente comando que mostrará la versión en ejecución.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker --version</span></span></code></pre></td></tr></table>
</div>
</div>
<p><img alt="Ventana de terminal mostrando la versión de Docker en ejecución" loading="lazy" src="/posts/instalar-docker-windows/images/docker_version.png"></p>
<p>Opcionalmente, se puede lanzar un contenedor de prueba que mostrará un mensaje de bienvenida con el siguiente comando.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker run hello-world</span></span></code></pre></td></tr></table>
</div>
</div>
<p><img alt="Ventana de terminal con una ejecución del contenedor Hello World" loading="lazy" src="/posts/instalar-docker-windows/images/docker_hello.png"></p>
<h1 id="conclusión">Conclusión</h1>
<p>Con Docker Desktop instalado en Windows ya tenemos un entorno preparado para trabajar con contenedores de forma local. A partir de ahí ya se pueden crear y ejecutar contenedores e integrar Docker en proyectos de desarrollo. Las posibilidades son infinitas y con cierta complejidad si queremos especializarnos, pero la potencia que ofrecen los contenedores, aun con configuraciones básicas, ya son muy grandes.</p>
]]></content:encoded></item><item><title>Compilar una aplicación Java usando Maven</title><link>/posts/compilar-app-maven/</link><pubDate>Fri, 02 Jan 2026 19:40:50 +0100</pubDate><guid>/posts/compilar-app-maven/</guid><description>&lt;p&gt;Aunque hoy día existen muy buenos IDEs con numerosos plugins que permiten centrarse en el código y en el desarrollo de la propia aplicación, es útil también &lt;strong&gt;conocer qué hacen internamente y poder lanzar tareas de forma más cercana a cómo se haría en un entorno de producción&lt;/strong&gt;.&lt;/p&gt;
&lt;h1 id="objetivo"&gt;Objetivo&lt;/h1&gt;
&lt;p&gt;Se trata de &lt;strong&gt;compilar y desplegar una aplicación Spring Boot con Maven&lt;/strong&gt; utilizando los comandos correspondientes a Maven y Java.&lt;/p&gt;
&lt;h1 id="prerrequisitos"&gt;Prerrequisitos&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;1. Tener instalado y configurado Java&lt;/strong&gt;. Para comprobarlo, lanzamos el siguiente comando en una terminal:&lt;/p&gt;</description><content:encoded><![CDATA[<p>Aunque hoy día existen muy buenos IDEs con numerosos plugins que permiten centrarse en el código y en el desarrollo de la propia aplicación, es útil también <strong>conocer qué hacen internamente y poder lanzar tareas de forma más cercana a cómo se haría en un entorno de producción</strong>.</p>
<h1 id="objetivo">Objetivo</h1>
<p>Se trata de <strong>compilar y desplegar una aplicación Spring Boot con Maven</strong> utilizando los comandos correspondientes a Maven y Java.</p>
<h1 id="prerrequisitos">Prerrequisitos</h1>
<p><strong>1. Tener instalado y configurado Java</strong>. Para comprobarlo, lanzamos el siguiente comando en una terminal:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">java -version</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Que debe devolver una salida similar a la siguiente:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">openjdk version &#34;25.0.1&#34; 2025-10-21 LTS
</span></span><span class="line"><span class="cl">OpenJDK Runtime Environment Temurin-25.0.1+8 (build 25.0.1+8-LTS)
</span></span><span class="line"><span class="cl">OpenJDK 64-Bit Server VM Temurin-25.0.1+8 (build 25.0.1+8-LTS, mixed mode, sharing)</span></span></code></pre></td></tr></table>
</div>
</div>
<p><strong>2. Tener instalado y configurado Maven.</strong> Para ello, se puede seguir el siguiente artículo: <a href="/posts/instalar-maven-windows/">Pasos para instalar Maven en Windows</a></p>
<p><strong>3. Tener una aplicación Java que haga uso de Maven</strong>. Se puede usar la desarrollada en el siguiente artículo: <a href="/posts/spring-boot-hello-world/">Spring Boot Hello World</a></p>
<h1 id="compilar-el-proyecto">Compilar el proyecto</h1>
<ol>
<li>Navegar hasta la ruta del proyecto donde está el fichero pom.xml</li>
</ol>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> ruta/a/tu/proyecto</span></span></code></pre></td></tr></table>
</div>
</div>
<ol start="2">
<li>Limpiar y empaquetar usando el comando <code>mvn</code></li>
</ol>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mvn clean package</span></span></code></pre></td></tr></table>
</div>
</div>
<ul>
<li><code>clean</code> → borra compilaciones previas</li>
<li><code>package</code> → compila + ejecuta tests + genera el <strong>JAR</strong></li>
</ul>
<p>Si todo va bien, la consola mostrará un mensaje de &lsquo;BUILD SUCCESS&rsquo;.</p>
<h1 id="localización-del-artefacto">Localización del artefacto</h1>
<p>Si la compilación ha finalizado correctamente, se habrá generado el fichero <code>jar</code> dentro de la carpeta <code>target</code> dentro de la estructura de carpetas del proyecto. Pero en el caso de una aplicación Spring Boot, nos encontraremos con dos ficheros, un <code>.jar</code> y adicionalmente un <code>.jar.original</code>.</p>
<p><img alt="Ficheros jar generados al compilar la aplicación con Maven" loading="lazy" src="/posts/compilar-app-maven/images/compilar-app-java-maven-jars.png"></p>
<p>El <code>.jar</code> es el que debemos usar para desplegar, ya que contiene:</p>
<ul>
<li>El <strong>código de la aplicación compilado</strong></li>
<li><strong>TODAS</strong> las dependencias</li>
<li>El cargador de Spring Boot</li>
<li>Un <code>Main-Class</code> ejecutable</li>
</ul>
<p>El <code>.jar.original</code> por contra, no se usa para desplegar, ya que:</p>
<ul>
<li>Es el <strong>JAR “normal” de Maven</strong></li>
<li>Solo contiene el código compilado</li>
<li><strong>No incluye dependencias</strong></li>
<li><strong>No es ejecutable</strong></li>
</ul>
<p>Lo que ha sucedido es Maven ha creado inicialmente el <code>jar</code> estándar <code>SpringBootHelloWorld-0.0.1-SNAPSHOT.jar</code>. Posteriormente, el plugin de Spring Boot lo renombra a <code>SpringBootHelloWorld-0.0.1-SNAPSHOT.jar.original</code> dejándolo como respaldo interno del proceso y realiza un reempaquetado generando el <code>.jar</code> que contiene todo lo necesario para que sea ejecutable.</p>
<h1 id="despliegue-de-la-aplicación">Despliegue de la aplicación</h1>
<p>Una vez localizado el fichero <code>.jar</code> se ejecuta mediante el comando <code>java</code> de la siguiente manera:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">java -jar target/SpringBootHelloWorld-0.0.1-SNAPSHOT.jar</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Mostrando la consola algo similar a lo que se ve en la siguiente imagen.</p>
<p><img alt="Aplicación en ejecución tras lanzar el jar" loading="lazy" src="/posts/compilar-app-maven/images/compilar-app-java-maven-ejecucion.png"></p>
<p>Y en este caso, se podrá probar que la aplicación funciona accediendo a un navegador.</p>
<p><img alt="Probando la aplicación en el navegador" loading="lazy" src="/posts/compilar-app-maven/images/compilar-app-java-maven-test.png"></p>
<h1 id="conclusiones">Conclusiones</h1>
<p>Este enfoque permite comprender mejor el ciclo de construcción de una aplicación Java, identificar el artefacto correcto para su despliegue y ganar autonomía a la hora de ejecutar la aplicación en otros entornos.</p>
<p>Entender qué genera Maven y cómo Spring Boot reempaqueta el artefacto final es un paso fundamental antes de abordar escenarios más avanzados como el despliegue en contenedores Docker o en servidores de producción.</p>
]]></content:encoded></item><item><title>Spring Boot Hello World</title><link>/posts/spring-boot-hello-world/</link><pubDate>Thu, 01 Jan 2026 12:29:15 +0100</pubDate><guid>/posts/spring-boot-hello-world/</guid><description>&lt;p&gt;&lt;a href="https://spring.io/projects/spring-boot"&gt;Spring Boot&lt;/a&gt; es en la actualidad un &lt;strong&gt;estándar en el desarrollo de aplicaciones Java&lt;/strong&gt;, con mucho protagonismo en el mundo empresarial. Con una &lt;strong&gt;configuración inicial simplificada e inteligente&lt;/strong&gt; permite levantar servicios en minutos. Incluye un servidor embebido y un ecosistema muy completo de &lt;em&gt;starters&lt;/em&gt; que simplifican las dependencias necesarias para el desarrollo de muchos tipos de aplicaciones.&lt;/p&gt;
&lt;h1 id="objetivo"&gt;Objetivo&lt;/h1&gt;
&lt;p&gt;Para ejemplificar esta simplicidad, se desarrolla un servicio web con un solo endpoint que devuelva un mensaje de &lt;em&gt;Hello World&lt;/em&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<p><a href="https://spring.io/projects/spring-boot">Spring Boot</a> es en la actualidad un <strong>estándar en el desarrollo de aplicaciones Java</strong>, con mucho protagonismo en el mundo empresarial. Con una <strong>configuración inicial simplificada e inteligente</strong> permite levantar servicios en minutos. Incluye un servidor embebido y un ecosistema muy completo de <em>starters</em> que simplifican las dependencias necesarias para el desarrollo de muchos tipos de aplicaciones.</p>
<h1 id="objetivo">Objetivo</h1>
<p>Para ejemplificar esta simplicidad, se desarrolla un servicio web con un solo endpoint que devuelva un mensaje de <em>Hello World</em>.</p>
<h1 id="creación-del-proyecto-base">Creación del proyecto base</h1>
<p>El ecosistema Spring cuenta con una herramienta que facilita la creación de la estructura base del proyecto, se trata de <a href="https://start.spring.io/">Spring initializr</a>.</p>
<p>Ésta genera la estructura básica de un proyecto Spring Boot en segundos. Permite seleccionar la versión de Spring, el lenguaje, el <em>build system</em> (Maven o Gradle) y las dependencias necesarias, creando automáticamente un proyecto listo para abrir en cualquier IDE.</p>
<p>Para el <em>Hello World</em> solo es necesario incluir la dependencia de <code>Spring Web</code>.</p>
<p><img alt="Spring Initializr" loading="lazy" src="/posts/spring-boot-hello-world/images/spring-initializr.png"></p>
<p>Al pulsar en <em>GENERATE</em> se descarga un fichero comprimido con el proyecto que cuenta con la estructura y componentes mínimos para importar en cualquier IDE y desplegar la aplicación.</p>
<p><img alt="Proyecto generado con Spring Initializr" loading="lazy" src="/posts/spring-boot-hello-world/images/spring-initializr-project.png"></p>
<p>Este proyecto se puede importar o abrir desde cualquier IDE que cuente con soporte para desarrollo de aplicaciones Spring Boot. Para el ejemplo, se utiliza IntelliJ.</p>
<p><img alt="Aplicación generada por Spring Initializr" loading="lazy" src="/posts/spring-boot-hello-world/images/spring-boot-app.png"></p>
<h1 id="creación-del-controlador">Creación del controlador</h1>
<p>Un controlador en Spring <strong>es el componente encargado de recibir las peticiones HTTP, procesarlas y devolver una respuesta</strong>. Actúa como punto de entrada de la aplicación, mapeando URLs y métodos HTTP (GET, POST, etc.) a métodos Java. Separa la lógica de exposición de la API del resto de la aplicación y su lógica, que se conformará con otro tipo de componentes que se utilizarán para obtener respuestas más complejas.</p>
<h2 id="anotaciones-restcontroller-y-getmapping">Anotaciones RestController y GetMapping</h2>
<p>Las anotaciones en Spring <strong>son etiquetas que se colocan sobre clases y métodos para decirle al framework qué papel cumple cada uno y cómo deben actuar</strong>, sin necesidad de escribir código adicional de configuración.</p>
<h3 id="restcontroller">RestController</h3>
<p>Es la anotación que marca una clase como <strong>un componente que expone endpoints REST</strong>. Todo lo que devuelvan sus métodos será serializado automáticamente (Strings, JSONs, etc.).</p>
<h3 id="getmapping">GetMapping</h3>
<p>Asocia un método a <strong>una petición HTTP GET y a una ruta concreta</strong> que se debe especificar como un parámetro de la propia anotación.</p>
<h2 id="ejemplo-completo">Ejemplo completo</h2>
<p>Para disponer de un endpoint bajo la ruta <code>/hello</code> que responda a una petición GET, bastaría con completar el Controlador <code>HelloController</code> de la siguiente forma:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nn">dev.juanfbermejo.SpringBootHelloWorld</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.springframework.web.bind.annotation.GetMapping</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.springframework.web.bind.annotation.RestController</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nd">@RestController</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">HelloController</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nd">@GetMapping</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">hello</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="s">&#34;Hello World from Spring Boot!&#34;</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<h1 id="prueba-de-la-aplicación">Prueba de la aplicación</h1>
<p>Para probar la aplicación y ver el <em>HelloWorld</em> bastaría con ejecutar en nuestro IDE la clase anotada con <code>SpringBootApplication</code>.</p>
<p>En la consola veremos aparecer varios logs que nos indicarán:</p>
<ul>
<li>Inicialización del servidor web (Tomcat por defecto) y el puerto en el que estará escuchando para recibir las peticiones (8080 por defecto)</li>
<li>Inicialización correcta de la propia aplicación.</li>
</ul>
<p>En ese momento, podremos abrir un navegador web e intentar cargar la url <code>http://localhost:8080/hello</code> lo cual realiza por defecto una petición GET a la ruta, devolviendo en la pantalla el texto de <em>HelloWorld</em>.</p>
<p><img alt="Hello World con Spring Boot en el navegador" loading="lazy" src="/posts/spring-boot-hello-world/images/spring-boot-hello.png"></p>
<h1 id="conclusiones">Conclusiones</h1>
<p>Montar un <em>Hello World</em> en Spring Boot es rápido y sencillo, y un buen punto de partida para entender las piezas que lo hacen posible. Las anotaciones son uno de los pilares del framework, y dominar su significado permite construir aplicaciones más limpias, expresivas y mantenibles.</p>
]]></content:encoded></item><item><title>Pasos para instalar Maven en Windows</title><link>/posts/instalar-maven-windows/</link><pubDate>Fri, 26 Dec 2025 17:10:36 +0100</pubDate><guid>/posts/instalar-maven-windows/</guid><description>&lt;h1 id="qué-es-maven"&gt;¿Qué es Maven?&lt;/h1&gt;
&lt;p&gt;Apache Maven es una &lt;strong&gt;herramienta de gestión y automatización de proyectos Java.&lt;/strong&gt; Su objetivo principal es &lt;strong&gt;simplificar ciertas tareas de desarrollo&lt;/strong&gt; como la compilación, la gestión de dependencias, ejecución de test y generación de artefactos (como pueden ser los archivos JAR).&lt;/p&gt;
&lt;p&gt;La configuración del proyecto se centraliza en el fichero &lt;code&gt;pom.xml&lt;/code&gt; (&lt;em&gt;Project Object Model&lt;/em&gt;) a partir de la cual, Maven se encarga de descargar librerías y ejecutar las tareas correspondientes.&lt;/p&gt;</description><content:encoded><![CDATA[<h1 id="qué-es-maven">¿Qué es Maven?</h1>
<p>Apache Maven es una <strong>herramienta de gestión y automatización de proyectos Java.</strong> Su objetivo principal es <strong>simplificar ciertas tareas de desarrollo</strong> como la compilación, la gestión de dependencias, ejecución de test y generación de artefactos (como pueden ser los archivos JAR).</p>
<p>La configuración del proyecto se centraliza en el fichero <code>pom.xml</code> (<em>Project Object Model</em>) a partir de la cual, Maven se encarga de descargar librerías y ejecutar las tareas correspondientes.</p>
<p>Maven se ha convertido en una herramienta esencial en el desarrollo de aplicaciones Java.</p>
<h1 id="instalación-de-maven-en-windows">Instalación de Maven en Windows</h1>
<p>Aunque Windows quizás no es el mejor sistema para utilizarlo en desarrollo de software, y la instalación de algunas herramientas implica cierta dificultad, en el caso de Maven basta con seguir un poco pasos para tenerlo funcionando.</p>
<h2 id="comprobación-previa-a-la-instalación">Comprobación previa a la instalación</h2>
<p>En primer lugar, se puede verificar si el equipo ya cuenta con una instalación de Maven correctamente configurada. Para ello, basta con abrir una consola de terminal y ejecutar el siguiente comando:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mvn -version</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Si no está instalado o configurado, obtendremos un mensaje que nos avisa de que el sistema no reconoce el comando:</p>
<p><img alt="Maven no está instalado" loading="lazy" src="/posts/instalar-maven-windows/images/maven-no-instalado.png"></p>
<h2 id="descarga-de-maven">Descarga de Maven</h2>
<p>Desde la web <a href="https://maven.apache.org/download.cgi">https://maven.apache.org/download.cgi</a> descargamos un &lsquo;<em>Binary zip archive</em>&rsquo;, que es un fichero zip que contiene lo necesario para ejecutar Maven.</p>
<p>Lo descomprimimos en una ruta de nuestro equipo donde queramos dejarlo instalado. Por ejemplo en  <code>C:\user\jfber\Maven\apache-maven-3.9.12</code></p>
<p><img alt="Carpeta de Maven en el sistema" loading="lazy" src="/posts/instalar-maven-windows/images/maven-instalacion.png"></p>
<h2 id="configuración-de-sistema">Configuración de sistema</h2>
<p>Ahora hay que configurar el sistema para que sepa donde localizar el ejecutable de Maven y que se pueda invocar desde cualquier ruta del sistema.</p>
<p>En primer lugar, hay que configurar una variable de entorno llamada <code>MAVEN_HOME</code> cuyo valor será la ruta completa de la carpeta de Maven creada en el paso anterior.</p>
<p>En el sistema Windows, en <code>Panel de control → Sistema → Editar las variables de entorno del Sistema</code> se crea la nueva variable:</p>
<p><img alt="Configurando la variable MAVEN_HOME" loading="lazy" src="/posts/instalar-maven-windows/images/maven-config-var.png"></p>
<p>Una vez configurada la variable que almacena la ruta donde se encuentra instalado Maven, se incluye la ruta del ejecutable en la variable <code>Path</code> de forma que se pueda ejecutar Maven desde cualquier ubicación del sistema.</p>
<p>En la variable <code>Path</code> se incluye el siguiente valor, que hace uso de la variable declarada anteriormente:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">%MAVEN_HOME%\bin</span></span></code></pre></td></tr></table>
</div>
</div>
<p><img alt="Incluyendo Maven en el path" loading="lazy" src="/posts/instalar-maven-windows/images/maven-config-path.png"></p>
<h2 id="comprobación-de-la-instalación">Comprobación de la instalación</h2>
<p>Una vez completada la configuración, se debe reiniciar la terminal y se vuelve a lanzar el comando:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mvn -version</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Si todo está correcto, ahora muestra la información sobre la versión de Maven instalada y el sistema está listo para comenzar a usar la herramienta.</p>
<p><img alt="Maven funcionando correctamente" loading="lazy" src="/posts/instalar-maven-windows/images/maven-instalado.png"></p>
]]></content:encoded></item></channel></rss>