<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[ATopeCode]]></title><description><![CDATA[Programación Java & Frikadas]]></description><link>http://www.atopecode.net/</link><image><url>http://www.atopecode.net/favicon.png</url><title>ATopeCode</title><link>http://www.atopecode.net/</link></image><generator>Ghost 4.41</generator><lastBuildDate>Thu, 30 Apr 2026 06:49:54 GMT</lastBuildDate><atom:link href="http://www.atopecode.net/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Arquitectura Hexagonal en Java con Spring Boot: API REST]]></title><description><![CDATA[ Arquitectura Hexagonal en Java con Spring Boot: API REST]]></description><link>http://www.atopecode.net/arquitectura-hexagonal-en-java-con-spring-boot-api-rest/</link><guid isPermaLink="false">68693fc019015564a2039cc2</guid><category><![CDATA[Java]]></category><category><![CDATA[Backend]]></category><category><![CDATA[Clean Architecture]]></category><category><![CDATA[Hexagonal Architecture]]></category><category><![CDATA[SpringBoot]]></category><category><![CDATA[Spring Framework]]></category><dc:creator><![CDATA[Silverio Martínez García]]></dc:creator><pubDate>Sat, 05 Jul 2025 16:01:43 GMT</pubDate><media:content url="http://www.atopecode.net/content/images/2025/07/d0f68b02cc239199e262bbe08fd13743.jpg" medium="image"/><content:encoded><![CDATA[<img src="http://www.atopecode.net/content/images/2025/07/d0f68b02cc239199e262bbe08fd13743.jpg" alt="Arquitectura Hexagonal en Java con Spring Boot: API REST"><p>En este art&#xED;culo vamos a construir una peque&#xF1;a aplicaci&#xF3;n de gesti&#xF3;n de tareas utilizando <strong>Arquitectura Hexagonal (Ports &amp; Adapters)</strong> con <strong>Java y Spring Boot</strong>.</p><p>La idea es aislar completamente la l&#xF3;gica de negocio (nuestro dominio) del framework y detalles de infraestructura (como el acceso a base de datos o los controladores REST). Esto nos permite tener un dominio limpio, f&#xE1;cil de testear y muy adaptable.</p><p>En este ejemplo, se han separado los casos de uso tanto en la capa de &apos;infraestructure&apos; (entrada - RestControllers) como en la capa de &apos;application&apos;. Se utiliza un servicio espec&#xED;fico para cada caso de uso: uno dedicado a la creaci&#xF3;n de tareas (<em>createTask</em>) y otro encargado de la obtenci&#xF3;n de todas las tareas (<em>getAllTasks</em>). Esta separaci&#xF3;n permite una mayor modularidad y separaci&#xF3;n de responsabilidad en cada componente, facilitando el mantenimiento, la escalabilidad y las pruebas unitarias. Al aislar cada caso de uso, evitamos mezclas innecesarias de l&#xF3;gica y conseguimos una arquitectura m&#xE1;s alineada con los principios de dise&#xF1;o limpio y la responsabilidad &#xFA;nica.</p><p>En este ejemplo utilizaremos una B.D. en memoria H2.</p><p>En este ejemplo no vamos a utilizar DDD (Domain Driven Design) para no complicar demasiado y centrarnos directamente en las ventajas que aporta la Arquitectura Hexagonal. El uso de DDD en la Arquitectura Hexagonal es opcional y se recomienda su uso cuando el dominio tiene bastante l&#xF3;gica. Cuando las entidades de dominio act&#xFA;an casi en su totalidad como DTOs (Data Transfer Objects) y no tienen l&#xF3;gica, se suele obviar el uso de DDD y se dice que el proyecto tiene un Dominio An&#xE9;mico.</p><h3 id="separaci%C3%B3n-entre-el-modelo-de-dominio-y-el-modelo-de-base-de-datos">Separaci&#xF3;n entre el modelo de dominio y el modelo de base de datos:</h3><p>Se debe mantener separado el <strong>modelo de dominio</strong> (la clase &apos;Task&apos; que representa la l&#xF3;gica del negocio) del <strong>modelo de persistencia</strong> (&apos;TaskEntity&apos;, usado por JPA). Aunque puedan parecer similares, tienen responsabilidades distintas: el dominio expresa reglas y conceptos del negocio, mientras que la definici&#xF3;n de la entidad de persistencia depende de como se guardan los datos f&#xED;sicamente en B.D. Esto se ve m&#xE1;s claramente si se utilizase DDD ya que la entidad de dominio &apos;Task&apos; tendr&#xED;a l&#xF3;gica y se diferenciar&#xED;a cl&#xE1;ramente de la entidad de persistencia que carecer&#xED;a de dicha l&#xF3;gica.</p><p>Esta separaci&#xF3;n evita que detalles t&#xE9;cnicos (como anotaciones JPA, IDs autogenerados o relaciones) contaminen la l&#xF3;gica del negocio. Tambi&#xE9;n nos permite <strong>modificar la persistencia sin tocar el n&#xFA;cleo de la aplicaci&#xF3;n</strong>, e incluso cambiar de base de datos o usar almacenamiento alternativo sin afectar al dominio. Esto mejora mucho la <strong>cohesi&#xF3;n, claridad y mantenibilidad</strong> del proyecto a largo plazo.</p><h3 id="separaci%C3%B3n-de-springboot-en-la-infraestructura">Separaci&#xF3;n de SpringBoot en la infraestructura:</h3><p>En la siguiente ejemplo se ha estructurado el c&#xF3;digo para que todas las <strong>anotaciones y configuraciones espec&#xED;ficas de Spring Boot</strong> (como <code>@Component</code>, <code>@RestController</code>, <code>@Bean</code>, etc.) est&#xE9;n contenidas exclusivamente en la capa de <strong>infraestructura</strong>. Esto no es casual: forma parte del principio de <strong>independencia del dominio</strong> que promueve la &apos;Arquitectura Hexagonal&apos;.</p><p>Gracias a esta separaci&#xF3;n, las capas de <strong>dominio</strong> y <strong>aplicaci&#xF3;n</strong> no dependen de ning&#xFA;n framework ni librer&#xED;a externa, lo que las hace <strong>m&#xE1;s testeables, reutilizables y desacopladas</strong>. Podr&#xED;amos usar exactamente el mismo n&#xFA;cleo de negocio en una app de consola, escritorio, REST o eventos, simplemente conectando adaptadores distintos. El resultado es un sistema m&#xE1;s flexible y sostenible a largo plazo.</p><p></p><p><strong>Estructura de paquetes:</strong></p><!--kg-card-begin: markdown--><pre><code class="language-text">src/
&#x2514;&#x2500;&#x2500; main/
    &#x2514;&#x2500;&#x2500; java/
        &#x2514;&#x2500;&#x2500; net/atopecode/todo/
            &#x251C;&#x2500;&#x2500; domain/
            &#x2502;   &#x251C;&#x2500;&#x2500; model/
            &#x2502;   &#x2502;   &#x2514;&#x2500;&#x2500; Task.java
            &#x2502;   &#x2514;&#x2500;&#x2500; port/
            &#x2502;       &#x251C;&#x2500;&#x2500; TaskRepository.java
            &#x2502;       &#x251C;&#x2500;&#x2500; CreateTaskService.java
            &#x2502;       &#x2514;&#x2500;&#x2500; ListTasksService.java
            &#x251C;&#x2500;&#x2500; application/
            &#x2502;   &#x251C;&#x2500;&#x2500; create/
            &#x2502;   &#x2502;   &#x2514;&#x2500;&#x2500; CreateTaskServiceImpl.java
            &#x2502;   &#x2514;&#x2500;&#x2500; list/
            &#x2502;       &#x2514;&#x2500;&#x2500; ListTasksServiceImpl.java
            &#x251C;&#x2500;&#x2500; infrastructure/
            &#x2502;   &#x251C;&#x2500;&#x2500; input/
            &#x2502;   &#x2502;   &#x2514;&#x2500;&#x2500; rest/
            &#x2502;   &#x2502;       &#x251C;&#x2500;&#x2500; create/
            &#x2502;   &#x2502;       &#x2502;   &#x2514;&#x2500;&#x2500; CreateTaskController.java
            &#x2502;   &#x2502;       &#x2514;&#x2500;&#x2500; list/
            &#x2502;   &#x2502;           &#x2514;&#x2500;&#x2500; ListTasksController.java
            &#x2502;   &#x2514;&#x2500;&#x2500; output/
            &#x2502;       &#x2514;&#x2500;&#x2500; persistence/
            &#x2502;           &#x251C;&#x2500;&#x2500; TaskEntity.java
            &#x2502;           &#x251C;&#x2500;&#x2500; JpaTaskRepositoryAdapter.java
            &#x2502;           &#x2514;&#x2500;&#x2500; SpringDataTaskRepository.java
            &#x2514;&#x2500;&#x2500; MainApplication.java</code></pre>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><p>pom.xml<br>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd"></project></p>
<pre><code>&lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;

&lt;groupId&gt;net.atopecode&lt;/groupId&gt;
&lt;artifactId&gt;todo-hexagonal&lt;/artifactId&gt;
&lt;version&gt;1.0.0&lt;/version&gt;
&lt;packaging&gt;jar&lt;/packaging&gt;

&lt;properties&gt;
    &lt;java.version&gt;17&lt;/java.version&gt;
    &lt;spring-boot.version&gt;3.2.0&lt;/spring-boot.version&gt;
&lt;/properties&gt;

&lt;dependencies&gt;
    &lt;!-- Spring Boot Starter Web --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
    &lt;/dependency&gt;

    &lt;!-- Spring Boot Starter Data JPA --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-data-jpa&lt;/artifactId&gt;
    &lt;/dependency&gt;

    &lt;!-- H2 In-Memory Database --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;com.h2database&lt;/groupId&gt;
        &lt;artifactId&gt;h2&lt;/artifactId&gt;
        &lt;scope&gt;runtime&lt;/scope&gt;
    &lt;/dependency&gt;

    &lt;!-- Test --&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
        &lt;scope&gt;test&lt;/scope&gt;
    &lt;/depend
</code></pre>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><p>application.properties</p>
<pre><code class="language-properties"># Configuraci&#xF3;n de la base de datos H2 en memoria
spring.datasource.url=jdbc:h2:mem:todo-db;DB_CLOSE_DELAY=-1
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

# JPA / Hibernate
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

# Consola H2 habilitada (opcional para desarrollo)
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
</code></pre>
<!--kg-card-end: markdown--><ol><li><strong>Capa de Dominio:</strong></li></ol><!--kg-card-begin: html-->domain/model/Task.java

<pre><code class="java">
package net.atopecode.todo.domain.model;

public class Task {
    private String id;
    private String description;
    private boolean completed;

    public Task(String id, String description, boolean completed) {
        this.id = id;
        this.description = description;
        this.completed = completed;
    }

    public String getId() { return id; }
    public String getDescription() { return description; }
    public boolean isCompleted() { return completed; }

    public void setCompleted(boolean completed) {
        this.completed = completed;
    }
}
</code></pre><!--kg-card-end: html--><!--kg-card-begin: html-->domain/port/TaskRepository.java
<pre><code class="java">
package net.atopecode.todo.domain.port;

import net.atopecode.todo.domain.model.Task;
import java.util.List;

public interface TaskRepository {
    void save(Task task);
    List&lt;Task&gt; findAll();
}
</code></pre><!--kg-card-end: html--><!--kg-card-begin: html-->domain/port/CreateTaskService.java
<pre><code class="java">
package net.atopecode.todo.domain.port;

public interface CreateTaskService {
    void createTask(String description);
}
</code></pre><!--kg-card-end: html--><!--kg-card-begin: html-->domain/port/ListTasksService.java
<pre><code class="java">
package net.atopecode.todo.domain.port;

import net.atopecode.todo.domain.model.Task;
import java.util.List;

public interface ListTasksService {
    List&lt;Task&gt; getAllTasks();
}
</code></pre><!--kg-card-end: html--><p><strong>2. Capa de Aplicaci&#xF3;n:</strong></p><!--kg-card-begin: html-->application/create/CreateTaskServiceImpl.java
<pre><code class="java">
package net.atopecode.todo.application.create;

import net.atopecode.todo.domain.model.Task;
import net.atopecode.todo.domain.port.CreateTaskService;
import net.atopecode.todo.domain.port.TaskRepository;

import java.util.UUID;

public class CreateTaskServiceImpl implements CreateTaskService {

    private final TaskRepository repository;

    public CreateTaskServiceImpl(TaskRepository repository) {
        this.repository = repository;
    }

    @Override
    public void createTask(String description) {
        Task task = new Task(UUID.randomUUID().toString(), description, false);
        repository.save(task);
    }
}
</code></pre><!--kg-card-end: html--><!--kg-card-begin: html-->application/list/ListTasksServiceImpl.java
<pre><code class="java">
package net.atopecode.todo.application.list;

import net.atopecode.todo.domain.model.Task;
import net.atopecode.todo.domain.port.ListTasksService;
import net.atopecode.todo.domain.port.TaskRepository;

import java.util.List;

public class ListTasksServiceImpl implements ListTasksService {

    private final TaskRepository repository;

    public ListTasksServiceImpl(TaskRepository repository) {
        this.repository = repository;
    }

    @Override
    public List&lt;Task&gt; getAllTasks() {
        return repository.findAll();
    }
}
</code></pre><!--kg-card-end: html--><p>3. Capa de Infraestructura:</p><!--kg-card-begin: html-->infrastructure/input/rest/create/CreateTaskController.java
<pre><code class="java">
package net.atopecode.todo.infrastructure.input.rest.create;

import net.atopecode.todo.domain.port.CreateTaskService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping(&quot;/tasks&quot;)
public class CreateTaskController {

    private final CreateTaskService createTaskService;

    public CreateTaskController(CreateTaskService createTaskService) {
        this.createTaskService = createTaskService;
    }

    @PostMapping
    public ResponseEntity&lt;Void&gt; create(@RequestBody CreateTaskDto dto) {
        createTaskService.createTask(dto.getDescription());
        return ResponseEntity.ok().build();
    }

    public static class CreateTaskDto {
        private String description;
        public String getDescription() { return description; }
        public void setDescription(String description) { this.description = description; }
    }
}
</code></pre><!--kg-card-end: html--><!--kg-card-begin: html-->infrastructure/input/rest/list/ListTasksController.java
<pre><code class="java">
package net.atopecode.todo.infrastructure.input.rest.list;

import net.atopecode.todo.domain.model.Task;
import net.atopecode.todo.domain.port.ListTasksService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.stream.Collectors;

@RestController
@RequestMapping(&quot;/tasks&quot;)
public class ListTasksController {

    private final ListTasksService listTasksService;

    public ListTasksController(ListTasksService listTasksService) {
        this.listTasksService = listTasksService;
    }

    @GetMapping
    public ResponseEntity&lt;List&lt;TaskDto&gt;&gt; list() {
        List&lt;Task&gt; tasks = listTasksService.getAllTasks();
        List&lt;TaskDto&gt; dtos = tasks.stream()
                .map(task -&gt; new TaskDto(task.getId(), task.getDescription(), task.isCompleted()))
                .collect(Collectors.toList());
        return ResponseEntity.ok(dtos);
    }

    public static class TaskDto {
        private String id;
        private String description;
        private boolean completed;

        public TaskDto(String id, String description, boolean completed) {
            this.id = id;
            this.description = description;
            this.completed = completed;
        }

        public String getId() { return id; }
        public String getDescription() { return description; }
        public boolean isCompleted() { return completed; }
    }
}

</code></pre><!--kg-card-end: html--><!--kg-card-begin: html-->infrastructure/output/persistence/TaskEntity.java
<pre><code class="java">
package net.atopecode.todo.infrastructure.output.persistence;

import jakarta.persistence.*;
import net.atopecode.todo.domain.model.Task;

@Entity
@Table(name = &quot;tasks&quot;)
public class TaskEntity {

    @Id
    private String id;
    private String description;
    private boolean completed;

    public TaskEntity() {}

    public TaskEntity(Task task) {
        this.id = task.getId();
        this.description = task.getDescription();
        this.completed = task.isCompleted();
    }

    public Task toDomain() {
        return new Task(id, description, completed);
    }

    // Getters y setters
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }

    public String getDescription() { return description; }
    public void setDescription(String description) { this.description = description; }

    public boolean isCompleted() { return completed; }
    public void setCompleted(boolean completed) { this.completed = completed; }
}
</code></pre><!--kg-card-end: html--><!--kg-card-begin: html-->infrastructure/output/persistence/SpringDataTaskRepository.java
<pre><code class="java">
package net.atopecode.todo.infrastructure.output.persistence;

import org.springframework.data.jpa.repository.JpaRepository;

public interface SpringDataTaskRepository extends JpaRepository&lt;TaskEntity, String&gt; {
}
</code></pre><!--kg-card-end: html--><!--kg-card-begin: html-->infrastructure/output/persistence/JpaTaskRepositoryAdapter.java
<pre><code class="java">
package net.atopecode.todo.infrastructure.output.persistence;

import net.atopecode.todo.domain.model.Task;
import net.atopecode.todo.domain.port.TaskRepository;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class JpaTaskRepositoryAdapter implements TaskRepository {

    private final SpringDataTaskRepository repository;

    public JpaTaskRepositoryAdapter(SpringDataTaskRepository repository) {
        this.repository = repository;
    }

    @Override
    public void save(Task task) {
        repository.save(new TaskEntity(task));
    }

    @Override
    public List&lt;Task&gt; findAll() {
        return repository.findAll()
                .stream()
                .map(TaskEntity::toDomain)
                .toList();
    }
}
</code></pre><!--kg-card-end: html--><p><strong>4. Main</strong></p><!--kg-card-begin: html-->MainApplication.java
<pre><code class="java">
package net.atopecode.todo;

import net.atopecode.todo.application.create.CreateTaskServiceImpl;
import net.atopecode.todo.application.list.ListTasksServiceImpl;
import net.atopecode.todo.domain.port.CreateTaskService;
import net.atopecode.todo.domain.port.ListTasksService;
import net.atopecode.todo.domain.port.TaskRepository;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication(scanBasePackages = &quot;net.atopecode.todo&quot;)
public class MainApplication {

    public static void main(String[] args) {
        SpringApplication.run(MainApplication.class, args);
    }

    @Bean
    public CreateTaskService createTaskService(TaskRepository taskRepository) {
        return new CreateTaskServiceImpl(taskRepository);
    }

    @Bean
    public ListTasksService listTasksService(TaskRepository taskRepository) {
        return new ListTasksServiceImpl(taskRepository);
    }
}
</code></pre><!--kg-card-end: html--><h3 id="conclusi%C3%B3n">Conclusi&#xF3;n</h3><p>La Arquitectura Hexagonal sirve para <strong>organizar el c&#xF3;digo de forma limpia, mantenible y flexible</strong>. En este art&#xED;culo hemos visto c&#xF3;mo separar el <strong>n&#xFA;cleo de negocio</strong> del resto de la infraestructura, permitiendo que nuestra l&#xF3;gica sea independiente de frameworks como SpringBoot o de detalles t&#xE9;cnicos como la base de datos.</p><p>Esta forma de estructurar aplicaciones facilita la <strong>escalabilidad, el testing aislado y la evoluci&#xF3;n del sistema</strong> facilitando el refactoring y la posiblidad de a&#xF1;adir nuevas funcionalidades sin esfuerzo. <br>Aunque al principio pueda parecer que este enfoque a&#xF1;ade complejidad, con el tiempo se convierte en un dise&#xF1;o m&#xE1;s robusto, ideal para proyectos medianos y grandes.</p><p>Los controladores, repositorios o incluso Spring Boot son adaptadores, lo importante es que el &apos;dominio&apos; (core) de tu aplicaci&#xF3;n sea <strong>simple, enfocado en el negocio y libre de acoplamientos innecesarios</strong>.</p><h3 id="art%C3%ADculos-relacionados">Art&#xED;culos relacionados:</h3><!--kg-card-begin: html-->Puedes ver en el art&#xED;culo anterior una implementaci&#xF3;n de Arquitectura Hexagonal en Java pero sin utilizar ning&#xFA;n Framework (sin SpringBoot):
<a href="http://www.atopecode.net/introduccion-a-la-arquitectura-hexagonal-en-java/" target="_blank" rel="noopener noreferrer">introduccion-a-la-arquitectura-hexagonal-en-java</a><!--kg-card-end: html-->]]></content:encoded></item><item><title><![CDATA[Introducción a la Arquitectura Hexagonal en Java]]></title><description><![CDATA[Introducción a la Arquitectura Hexagonal en Java]]></description><link>http://www.atopecode.net/introduccion-a-la-arquitectura-hexagonal-en-java/</link><guid isPermaLink="false">6869325b19015564a2039bff</guid><category><![CDATA[Java]]></category><category><![CDATA[Backend]]></category><category><![CDATA[Hexagonal Architecture]]></category><category><![CDATA[Clean Architecture]]></category><dc:creator><![CDATA[Silverio Martínez García]]></dc:creator><pubDate>Sat, 05 Jul 2025 14:40:49 GMT</pubDate><media:content url="http://www.atopecode.net/content/images/2025/07/562079c4ce4a9993fee28577d8eff139.jpg" medium="image"/><content:encoded><![CDATA[<img src="http://www.atopecode.net/content/images/2025/07/562079c4ce4a9993fee28577d8eff139.jpg" alt="Introducci&#xF3;n a la Arquitectura Hexagonal en Java"><p>La <strong>Arquitectura Hexagonal</strong>, tambi&#xE9;n conocida como <strong>Ports and Adapters</strong>, fue propuesta por Alistair Cockburn con el objetivo de crear aplicaciones independientes de frameworks, bases de datos, interfaces gr&#xE1;ficas o cualquier elemento externo. Su principal motivaci&#xF3;n es <strong>aislar el dominio</strong> de las dependencias externas.</p><h3 id="componentes-clave">Componentes Clave</h3><ul><li><strong>Dominio (Core)</strong>: Contiene la l&#xF3;gica de negocio pura. No depende de nada externo.</li><li><strong>Puertos (Ports)</strong>: Interfaces que definen c&#xF3;mo se comunica el dominio con el exterior.</li><li><strong>Adaptadores (Adapters)</strong>: Implementaciones concretas de los puertos, como controladores REST, repositorios, UIs, etc.</li></ul><!--kg-card-begin: markdown--><pre><code>  [REST Controller]         [CLI]
           \                  /
          [Entrada (Input Port)]
                   |
               [Dominio]
                   |
          [Salida (Output Port)]
           /              \
[Base de Datos]       [API externa]
</code></pre>
<!--kg-card-end: markdown--><p>En algunas ocasiones tambi&#xE9;n se le llama a los &apos;Input Port&apos; como &apos;Puertos Primarios&apos; y a los &apos;Output Port&apos; como &apos;Puertos Secundarios&apos;. Pero no son m&#xE1;s que nombres distintos para el mismo concepto.</p><h3 id="capas">Capas:</h3><ul><li><strong>Domain</strong>: Contiene el coraz&#xF3;n del negocio: entidades, l&#xF3;gica pura e interfaces (puertos) que definen qu&#xE9; necesita el dominio para funcionar. Esta capa <strong>no conoce ni depende de ninguna tecnolog&#xED;a</strong>, ni sabe nada sobre controladores, frameworks o bases de datos.</li><li><strong>Application:</strong> Orquesta los casos de uso: coordina las operaciones del dominio usando las interfaces (puertos) que este expone. Esta capa <strong>depende del dominio</strong>, pero <strong>no sabe nada de la infraestructura</strong> (como REST, JPA o controladores).</li><li><strong>Infraestructure:</strong> Contiene los adaptadores concretos que implementan lo que el dominio necesita (como persistencia o entrada de datos). Aqu&#xED; viven los controladores REST y los repositorios JPA. Esta capa <strong>s&#xED; depende de las anteriores</strong>, ya que necesita conocer los puertos del dominio para implementarlos, pero <strong>el flujo de dependencias no va al rev&#xE9;s</strong>.</li></ul><p>Esta separaci&#xF3;n garantiza un c&#xF3;digo modular, desacoplado, f&#xE1;cil de probar y de evolucionar sin romper el n&#xFA;cleo del negocio. </p><p>Como norma se establece que las capas superiores pueden utilizar clases de las capas inferiores pero no al rev&#xE9;s, dando por hecho que la capas ordenadas de inferior a superior son &apos;domain&apos;, &apos;application&apos; e &apos;infraestructure&apos;.</p><ul><li><strong>En la capa de &apos;domain&apos; no se debe utilizar ninguna clase de las capas superiores &apos;application&apos; e infraestructure&apos;.</strong></li><li><strong>En la capa de &apos;application&apos; s&#xED; se puede utilizar clases de la inferior capa &apos;domain&apos; pero no de la capa superior &apos;infraestructure&apos;.</strong></li><li><strong>En la capa de &apos;infraestructure&apos; como es la capa superior, puede utilizar clases de las capas inferiores &apos;domain&apos; y &apos;application&apos;.</strong></li></ul><h3 id="ejemplo-en-javagesti%C3%B3n-de-tareas">Ejemplo en Java - Gesti&#xF3;n de Tareas:</h3><p>Vamos a crear una aplicaci&#xF3;n simple que gestiona tareas (ToDo). Usaremos Java puro sin frameworks (como SpringBoot), para centrarnos en la estructura.</p><p>Simularemos la B.D. con un &apos;ArrayList&apos; en memoria.</p><p>Tampoco vamos a utilizar DDD (Domain Driven Design) para no complicar el ejemplo y centrarnos directamente en las ventajas que aporta la Arquitectura Hexagonal. El uso de DDD en la Arquitectura Hexagonal es opcional y se recomienda su uso cuando el dominio tiene bastante l&#xF3;gica. Cuando las entidades de dominio act&#xFA;an casi en su totalidad como DTOs (Data Transfer Objects) y no tienen l&#xF3;gica, se suele obviar el uso de DDD y se dice que el proyecto tiene un Dominio An&#xE9;mico.</p><p><strong>Estructura de paquetes:</strong></p><!--kg-card-begin: markdown--><pre><code class="language-text">src/
&#x2514;&#x2500;&#x2500; main/
    &#x2514;&#x2500;&#x2500; java/
        &#x2514;&#x2500;&#x2500; net/atopecode/todo/
            &#x251C;&#x2500;&#x2500; domain/
            &#x2502;   &#x251C;&#x2500;&#x2500; model/
            &#x2502;   &#x2514;&#x2500;&#x2500; port/
            &#x251C;&#x2500;&#x2500; application/
            &#x251C;&#x2500;&#x2500; infrastructure/
            &#x2502;   &#x251C;&#x2500;&#x2500; input/
            &#x2502;   &#x2514;&#x2500;&#x2500; output/
            &#x2514;&#x2500;&#x2500; MainApplication.java</code></pre>
<!--kg-card-end: markdown--><ol><li><strong>Capa de Dominio:</strong></li></ol><!--kg-card-begin: html-->domain/model/Task.java
<pre><code class="java">
package net.atopecode.todo.domain.model;

public class Task {
    private String id;
    private String description;
    private boolean completed;

    public Task(String id, String description, boolean completed) {
        this.id = id;
        this.description = description;
        this.completed = completed;
    }

    // Getters y setters
    public String getId() { return id; }
    public String getDescription() { return description; }
    public boolean isCompleted() { return completed; }

    public void setCompleted(boolean completed) {
        this.completed = completed;
    }
}

</code></pre><!--kg-card-end: html--><!--kg-card-begin: html-->domain/port/TaskRepository.java (Output Port)
<pre><code class="java">
package net.atopecode.todo.domain.port;

import net.atopecode.todo.domain.model.Task;
import java.util.List;

public interface TaskRepository {
    void save(Task task);
    List&lt;Task&gt; findAll();
}

</code></pre><!--kg-card-end: html--><p><strong>2. Capa de Aplicaci&#xF3;n:</strong></p><!--kg-card-begin: html-->application/TaskService.java (Input Port)
<pre><code class="java">
package net.atopecode.todo.application;

import net.atopecode.todo.domain.model.Task;
import java.util.List;

public interface TaskService {
    void createTask(String description);
    List&lt;Task&gt; getAllTasks();
}

</code></pre><!--kg-card-end: html--><!--kg-card-begin: html-->application/TaskServiceImpl.java
<pre><code class="java">
package net.atopecode.todo.application;

import net.atopecode.todo.domain.model.Task;
import net.atopecode.todo.domain.port.TaskRepository;

import java.util.List;
import java.util.UUID;

public class TaskServiceImpl implements TaskService {

    private final TaskRepository repository;

    public TaskServiceImpl(TaskRepository repository) {
        this.repository = repository;
    }

    @Override
    public void createTask(String description) {
        Task task = new Task(UUID.randomUUID().toString(), description, false);
        repository.save(task);
    }

    @Override
    public List&lt;Task&gt; getAllTasks() {
        return repository.findAll();
    }
}

</code></pre><!--kg-card-end: html--><p><strong>3. Capa de Infraestructura:</strong></p><!--kg-card-begin: html-->infrastructure/output/InMemoryTaskRepository.java
<pre><code class="java">
package net.atopecode.todo.infrastructure.output;

import net.atopecode.todo.domain.model.Task;
import net.atopecode.todo.domain.port.TaskRepository;

import java.util.ArrayList;
import java.util.List;

public class InMemoryTaskRepository implements TaskRepository {

    private final List&lt;Task&gt; tasks = new ArrayList&lt;&gt;();

    @Override
    public void save(Task task) {
        tasks.add(task);
    }

    @Override
    public List&lt;Task&gt; findAll() {
        return new ArrayList&lt;&gt;(tasks);
    }
}

</code></pre><!--kg-card-end: html--><!--kg-card-begin: html-->infrastructure/input/ConsoleAdapter.java
<pre><code class="java">
package net.atopecode.todo.infrastructure.input;

import net.atopecode.todo.application.TaskService;
import net.atopecode.todo.domain.model.Task;

public class ConsoleAdapter {

    private final TaskService taskService;

    public ConsoleAdapter(TaskService taskService) {
        this.taskService = taskService;
    }

    public void run() {
        taskService.createTask(&quot;Aprender Arquitectura Hexagonal&quot;);
        taskService.createTask(&quot;Publicar en atopecode.net&quot;);

        for (Task task : taskService.getAllTasks()) {
            System.out.println(&quot;- &quot; + task.getDescription());
        }
    }
}

</code></pre><!--kg-card-end: html--><p><strong>4. Main:</strong></p><!--kg-card-begin: html--><pre><code class="java">
package net.atopecode.todo;

import net.atopecode.todo.application.TaskService;
import net.atopecode.todo.application.TaskServiceImpl;
import net.atopecode.todo.domain.port.TaskRepository;
import net.atopecode.todo.infrastructure.input.ConsoleAdapter;
import net.atopecode.todo.infrastructure.output.InMemoryTaskRepository;

public class Main {
    public static void main(String[] args) {
        TaskRepository repository = new InMemoryTaskRepository();
        TaskService taskService = new TaskServiceImpl(repository);
        ConsoleAdapter console = new ConsoleAdapter(taskService);

        console.run();
    }
}

</code></pre><!--kg-card-end: html--><h2 id="conclusi%C3%B3n">Conclusi&#xF3;n</h2><p>Este ejemplo muestra c&#xF3;mo aplicar Arquitectura Hexagonal en un proyecto Java sin frameworks, organizando las clases en:</p><ul><li>El <strong>dominio</strong> es independiente del resto. Contiene la l&#xF3;gica de negocio y las interfaces.</li><li>Las <strong>interfaces</strong> definen contratos, no implementaciones. Orquesta los casos de uso del negocio.</li><li>Las <strong>implementaciones concretas</strong> (adaptadores) se pueden cambiar sin tocar la l&#xF3;gica de negocio. Se comunica con el exterior (UI, persistencia, etc.).</li></ul><p>Lo interesante es que puedes cambiar los adaptadores (por ejemplo, una consola por una API REST) sin modificar la l&#xF3;gica de negocio.</p><h3 id="art%C3%ADculos-relacionados">Art&#xED;culos relacionados:</h3><!--kg-card-begin: html-->Puedes ver en el pr&#xF3;ximo art&#xED;culo una implementaci&#xF3;n de Arquitectura Hexagonal en Java utilizando el framework SpringBoot:
<a href="http://www.atopecode.net/arquitectura-hexagonal-en-java-con-spring-boot-api-rest/" target="_blank" rel="noopener noreferrer">arquitectura-hexagonal-en-java-con-spring-boot-api-rest</a><!--kg-card-end: html-->]]></content:encoded></item><item><title><![CDATA[Alta cohesión. Mantén tu código limpio y mantenible.]]></title><description><![CDATA[Alta cohesión en nuestro código. Ventajas y explicación con código Java.]]></description><link>http://www.atopecode.net/alta-cohesion-manten-tu-codigo-limpio-y-mantenible/</link><guid isPermaLink="false">67256daf19015564a2039ad5</guid><category><![CDATA[Java]]></category><category><![CDATA[Backend]]></category><category><![CDATA[CleanCode]]></category><category><![CDATA[Solid]]></category><dc:creator><![CDATA[Silverio Martínez García]]></dc:creator><pubDate>Fri, 29 Nov 2024 15:14:32 GMT</pubDate><media:content url="http://www.atopecode.net/content/images/2024/11/alta-cohesion---100x600.png" medium="image"/><content:encoded><![CDATA[<img src="http://www.atopecode.net/content/images/2024/11/alta-cohesion---100x600.png" alt="Alta cohesi&#xF3;n. Mant&#xE9;n tu c&#xF3;digo limpio y mantenible."><p>En el mundo del desarrollo de software, uno de los principios clave para crear aplicaciones robustas, mantenibles y escalables es la <strong>cohesi&#xF3;n</strong>. La cohesi&#xF3;n mide cu&#xE1;n estrechamente est&#xE1;n relacionados los elementos dentro de una clase o m&#xF3;dulo. La <strong>alta cohesi&#xF3;n</strong> es un objetivo esencial en el dise&#xF1;o de clases y m&#xF3;dulos en lenguajes de programaci&#xF3;n orientados a objetos como <strong>Java</strong>.</p><h2 id="%C2%BFqu%C3%A9-es-la-alta-cohesi%C3%B3n">&#xBF;Qu&#xE9; es la Alta Cohesi&#xF3;n?</h2><p>En t&#xE9;rminos simples, <strong>alta cohesi&#xF3;n</strong> significa que las responsabilidades de una clase o m&#xF3;dulo est&#xE1;n bien definidas y est&#xE1;n estrechamente relacionadas entre s&#xED;. Una clase con alta cohesi&#xF3;n tiene un prop&#xF3;sito claro y est&#xE1; encargada de un conjunto limitado de tareas que est&#xE1;n directamente relacionadas.</p><p>Por el contrario, una clase con baja cohesi&#xF3;n tiene responsabilidades dispersas, lo que la hace dif&#xED;cil de entender, mantener y extender. Las clases altamente cohesionadas, en cambio, son m&#xE1;s f&#xE1;ciles de comprender, probar y modificar, ya que sus m&#xE9;todos y atributos est&#xE1;n fuertemente relacionados con un solo prop&#xF3;sito.</p><h3 id="caracter%C3%ADsticas-de-la-alta-cohesi%C3%B3n"><strong><strong>Caracter&#xED;sticas de la Alta Cohesi&#xF3;n</strong></strong>:</h3><ol><li><strong>Responsabilidad &#xFA;nica</strong>: La clase tiene una &#xFA;nica raz&#xF3;n para cambiar.</li><li><strong>Relaci&#xF3;n entre m&#xE9;todos y atributos</strong>: Los m&#xE9;todos y atributos de la clase est&#xE1;n relacionados con la misma funcionalidad.</li><li><strong>Facilidad de mantenimiento</strong>: Las clases con alta cohesi&#xF3;n son m&#xE1;s f&#xE1;ciles de modificar sin afectar otras partes del sistema.</li><li><strong>Mayor reutilizaci&#xF3;n</strong>: Las clases cohesionadas pueden ser reutilizadas m&#xE1;s f&#xE1;cilmente en diferentes contextos, ya que su funcionalidad est&#xE1; claramente definida.</li></ol><h2 id="%C2%BFpor-qu%C3%A9-es-importante-la-alta-cohesi%C3%B3n">&#xBF;Por qu&#xE9; es importante la Alta Cohesi&#xF3;n?</h2><p>Mantener alta cohesi&#xF3;n dentro de las clases tiene varios beneficios clave:</p><ul><li><strong>Mejora la legibilidad</strong>: Cuando una clase tiene un prop&#xF3;sito claro y sus m&#xE9;todos son coherentes entre s&#xED;, es mucho m&#xE1;s f&#xE1;cil de leer y entender.</li><li><strong>Facilita las pruebas unitarias</strong>: Las clases con alta cohesi&#xF3;n son m&#xE1;s f&#xE1;ciles de probar porque se centran en una sola responsabilidad, lo que simplifica la creaci&#xF3;n de pruebas.</li><li><strong>Simplifica el mantenimiento</strong>: Los cambios en una clase cohesionada son menos propensos a afectar otras partes del sistema.</li><li><strong>Fomenta el dise&#xF1;o modular</strong>: Al tener clases que se encargan de responsabilidades bien definidas, se facilita la modularizaci&#xF3;n y la separaci&#xF3;n de preocupaciones, lo que mejora la escalabilidad del sistema.</li></ul><h2 id="ejemplo-de-baja-cohesi%C3%B3n">Ejemplo de Baja Cohesi&#xF3;n</h2><p>Supongamos que tienes una clase que maneja tanto la persistencia de datos como la l&#xF3;gica de negocio. Esta clase estar&#xED;a haciendo varias cosas no relacionadas, lo que resulta en una <strong>baja cohesi&#xF3;n</strong>.</p><!--kg-card-begin: html--><pre><code class="java">
public class UsuarioManager {

    // M&#xE9;todo para gestionar la l&#xF3;gica de negocio de los usuarios
    public void registrarUsuario(String nombre, String email) {
        // L&#xF3;gica para registrar al usuario
        System.out.println(&quot;Usuario registrado: &quot; + nombre);
    }

    // M&#xE9;todo para persistir los datos en la base de datos
    public void guardarUsuarioEnBaseDeDatos(String nombre, String email) {
        // L&#xF3;gica para guardar en base de datos
        System.out.println(&quot;Usuario guardado en la base de datos: &quot; + nombre);
    }

    // M&#xE9;todo para enviar un correo al usuario
    public void enviarCorreoDeBienvenida(String email) {
        // L&#xF3;gica para enviar correo
        System.out.println(&quot;Correo de bienvenida enviado a: &quot; + email);
    }
}
</code></pre><!--kg-card-end: html--><p>En este ejemplo, la clase <code>UsuarioManager</code> est&#xE1; manejando m&#xFA;ltiples responsabilidades: l&#xF3;gica de negocio, persistencia y comunicaciones (correo). Esto genera una <strong>baja cohesi&#xF3;n</strong>, ya que no est&#xE1; centrada en un solo prop&#xF3;sito. Si decidimos cambiar la forma en que enviamos correos, afectar&#xED;a a la clase entera, lo que hace que el mantenimiento sea m&#xE1;s complicado.</p><h2 id="ejemplo-de-alta-cohesi%C3%B3n">Ejemplo de Alta Cohesi&#xF3;n</h2><p>Ahora, veamos c&#xF3;mo mejorar la cohesi&#xF3;n dividiendo las responsabilidades en clases m&#xE1;s peque&#xF1;as y espec&#xED;ficas.</p><!--kg-card-begin: html--><pre><code class="java">
public class UsuarioRegistrador {
    public void registrarUsuario(String nombre, String email) {
        // L&#xF3;gica para registrar al usuario
        System.out.println(&quot;Usuario registrado: &quot; + nombre);
    }
}

public class UsuarioRepositorio {
    public void guardarUsuarioEnBaseDeDatos(String nombre, String email) {
        // L&#xF3;gica para guardar en base de datos
        System.out.println(&quot;Usuario guardado en la base de datos: &quot; + nombre);
    }
}

public class CorreoDeBienvenida {
    public void enviarCorreo(String email) {
        // L&#xF3;gica para enviar correo
        System.out.println(&quot;Correo de bienvenida enviado a: &quot; + email);
    }
}

</code></pre><!--kg-card-end: html--><p>En este ejemplo, hemos dividido las responsabilidades en tres clases:</p><ol><li><strong>UsuarioRegistrador</strong>: Se encarga exclusivamente de la l&#xF3;gica de negocio relacionada con el registro de usuarios.</li><li><strong>UsuarioRepositorio</strong>: Se ocupa de la persistencia de los usuarios en la base de datos.</li><li><strong>CorreoDeBienvenida</strong>: Se encarga de enviar los correos electr&#xF3;nicos de bienvenida.</li></ol><p>Cada clase tiene una responsabilidad clara y bien definida, lo que hace que el c&#xF3;digo sea m&#xE1;s f&#xE1;cil de mantener y extender. Si, por ejemplo, necesitamos cambiar la manera en que se env&#xED;an los correos electr&#xF3;nicos, solo necesitaremos modificar la clase <code>CorreoDeBienvenida</code> sin tocar las otras clases.</p><h3 id="beneficios">Beneficios:</h3><ul><li><strong>Facilidad de mantenimiento</strong>: Cada clase tiene un prop&#xF3;sito claro, lo que reduce la posibilidad de introducir errores cuando realizamos cambios.</li><li><strong>Mayor modularidad</strong>: Si queremos agregar nuevas funcionalidades, como un sistema de notificaciones, podemos hacerlo sin afectar el resto del c&#xF3;digo.</li><li><strong>Mejor testabilidad</strong>: Al estar bien separadas las responsabilidades, se facilita la escritura de pruebas unitarias.</li></ul><h2 id="c%C3%B3mo-lograr-alta-cohesi%C3%B3n-en-java">C&#xF3;mo Lograr Alta Cohesi&#xF3;n en Java</h2><p>Para lograr alta cohesi&#xF3;n en Java, hay varias estrategias que puedes seguir:</p><ol><li><strong>Aplicar el principio de responsabilidad &#xFA;nica (SRP)</strong>: Cada clase debe tener una &#xFA;nica raz&#xF3;n para cambiar, lo que implica que debe centrarse en una sola tarea.</li><li><strong>Refactorizar cuando sea necesario</strong>: Si una clase empieza a asumir demasiadas responsabilidades, es momento de refactorizarla en clases m&#xE1;s peque&#xF1;as y especializadas.</li><li><strong>Usar clases peque&#xF1;as y enfocadas</strong>: Aseg&#xFA;rate de que cada clase tenga un &#xFA;nico prop&#xF3;sito y que todos sus m&#xE9;todos est&#xE9;n relacionados con esa responsabilidad.</li><li><strong>Composici&#xF3;n sobre herencia</strong>: A menudo, es m&#xE1;s eficiente componer objetos con responsabilidades bien definidas que intentar heredarlas de una clase base con m&#xFA;ltiples responsabilidades.</li></ol><h2 id="conclusi%C3%B3n">Conclusi&#xF3;n</h2><p>En resumen, la <strong>alta cohesi&#xF3;n</strong> es un principio esencial para crear software bien estructurado y f&#xE1;cil de mantener. Al seguir este principio, podemos crear clases en Java que sean claras, modulares y f&#xE1;ciles de probar, lo que a su vez facilita la evoluci&#xF3;n del sistema sin generar complicaciones innecesarias.</p><p>Recuerda, una clase con alta cohesi&#xF3;n tiene un prop&#xF3;sito claro, se enfoca en tareas espec&#xED;ficas y reduce la probabilidad de errores al hacer cambios. </p>]]></content:encoded></item><item><title><![CDATA[Bajo Acoplamiento: Mantén tu código limpio y mantenible]]></title><description><![CDATA[Bajo Acoplamiento en nuestro código. Ventajas y explicación con código Java.]]></description><link>http://www.atopecode.net/bajo-acoplamiento-clave-para-un-codigo-limpio-y-mantenible-2/</link><guid isPermaLink="false">672525b119015564a2039a5d</guid><category><![CDATA[Backend]]></category><category><![CDATA[Java]]></category><category><![CDATA[Solid]]></category><category><![CDATA[CleanCode]]></category><dc:creator><![CDATA[Silverio Martínez García]]></dc:creator><pubDate>Fri, 01 Nov 2024 23:27:46 GMT</pubDate><media:content url="http://www.atopecode.net/content/images/2024/11/bajo-acoplamiento---1000x600.png" medium="image"/><content:encoded><![CDATA[<h2 id="introducci%C3%B3n">Introducci&#xF3;n</h2><img src="http://www.atopecode.net/content/images/2024/11/bajo-acoplamiento---1000x600.png" alt="Bajo Acoplamiento: Mant&#xE9;n tu c&#xF3;digo limpio y mantenible"><p>En el mundo del desarrollo de software, uno de los principios m&#xE1;s importantes es el bajo acoplamiento. En este art&#xED;culo, exploraremos c&#xF3;mo este concepto se aplica en Java, sus beneficios y las mejores pr&#xE1;cticas para lograrlo. Un dise&#xF1;o con bajo acoplamiento no solo mejora la mantenibilidad del c&#xF3;digo, sino que tambi&#xE9;n facilita la escalabilidad y la reutilizaci&#xF3;n de componentes.</p><h2 id="%C2%BFqu%C3%A9-es-el-bajo-acoplamiento">&#xBF;Qu&#xE9; es el Bajo Acoplamiento?</h2><p>El bajo acoplamiento se refiere a la interdependencia m&#xED;nima entre diferentes m&#xF3;dulos o componentes de un sistema. En t&#xE9;rminos simples, significa que un cambio en un componente deber&#xED;a afectar lo menos posible a otros componentes. Este principio es fundamental en la programaci&#xF3;n orientada a objetos, donde se busca crear clases y objetos que sean lo m&#xE1;s independientes posible.</p><h3 id="beneficios-del-bajo-acoplamiento-en-java">Beneficios del Bajo Acoplamiento en Java</h3><ol><li><strong>Facilidad de Mantenimiento</strong>: Un sistema con bajo acoplamiento es m&#xE1;s f&#xE1;cil de modificar. Los desarrolladores pueden realizar cambios en una parte del c&#xF3;digo sin temor a romper otras partes.</li><li><strong>Reutilizaci&#xF3;n de C&#xF3;digo</strong>: Las clases con bajo acoplamiento son m&#xE1;s f&#xE1;cilmente reutilizables en otros proyectos. Esto ahorra tiempo y esfuerzo en el desarrollo.</li><li><strong>Escalabilidad</strong>: A medida que los proyectos crecen, un dise&#xF1;o de bajo acoplamiento permite agregar nuevas funcionalidades sin complicar el sistema existente.</li><li><strong>Pruebas M&#xE1;s Efectivas</strong>: Las unidades de c&#xF3;digo menos acopladas son m&#xE1;s sencillas de probar de manera independiente, lo que mejora la calidad del software.</li></ol><h2 id="estrategias-para-lograr-bajo-acoplamiento-en-java">Estrategias para Lograr Bajo Acoplamiento en Java</h2><h3 id="1-uso-de-interfaces">1. Uso de Interfaces</h3><p>Las interfaces en Java permiten definir contratos que las clases pueden implementar. Esto promueve el bajo acoplamiento, ya que las clases pueden interactuar a trav&#xE9;s de estas interfaces sin necesidad de conocer la implementaci&#xF3;n espec&#xED;fica.</p><!--kg-card-begin: html--><pre><code class="java">
public interface Animal {
    void hacerSonido();
}

public class Perro implements Animal {
    @Override
    public void hacerSonido() {
        System.out.println(&quot;Guau&quot;);
    }
}

public class Gato implements Animal {
    @Override
    public void hacerSonido() {
        System.out.println(&quot;Miau&quot;);
    }
}
</code></pre><!--kg-card-end: html--><p></p><h3 id="2-inyecci%C3%B3n-de-dependencias">2. Inyecci&#xF3;n de Dependencias</h3><p>La inyecci&#xF3;n de dependencias es una t&#xE9;cnica que permite desacoplar clases al inyectar sus dependencias desde el exterior en lugar de crearlas internamente. A continuaci&#xF3;n, se muestra c&#xF3;mo se puede implementar utilizando la interfaz <code>Animal</code>:</p><!--kg-card-begin: markdown--><pre><code class="java">
public interface Animal {
    void hacerSonido();
}

public class Perro implements Animal {
    @Override
    public void hacerSonido() {
        System.out.println(&quot;Guau&quot;);
    }
}

public class Gato implements Animal {
    @Override
    public void hacerSonido() {
        System.out.println(&quot;Miau&quot;);
    }
}

public class ServicioAnimal {
    private Animal animal;

    public ServicioAnimal(Animal animal) {
        this.animal = animal;
    }

    public void hacerSonidoDelAnimal() {
        animal.hacerSonido();
    }
}

// Uso de la inyecci&#xF3;n de dependencias
public class Main {
    public static void main(String[] args) {
        Animal perro = new Perro();
        ServicioAnimal servicio = new ServicioAnimal(perro);
        servicio.hacerSonidoDelAnimal(); // Imprime: Guau

        Animal gato = new Gato();
        ServicioAnimal servicioGato = new ServicioAnimal(gato);
        servicioGato.hacerSonidoDelAnimal(); // Imprime: Miau
    }
}
</code></pre><!--kg-card-end: markdown--><h3 id="3-patr%C3%B3n-de-dise%C3%B1o-observador">3. Patr&#xF3;n de Dise&#xF1;o Observador</h3><p>El patr&#xF3;n de dise&#xF1;o observador permite que un objeto notifique a otros objetos sobre cambios en su estado, reduciendo as&#xED; la dependencia directa entre ellos.</p><!--kg-card-begin: html--><pre><code class="java">
public interface Observador {
    void actualizar();
}

public class Sujeto {
    private List<observador> observadores = new ArrayList&lt;&gt;();

    public void agregarObservador(Observador observador) {
        observadores.add(observador);
    }

    public void notificarObservadores() {
        for (Observador observador : observadores) {
            observador.actualizar();
        }
    }
}
</observador></code></pre><!--kg-card-end: html--><h2 id="conclusi%C3%B3n">Conclusi&#xF3;n</h2><p>El bajo acoplamiento es un principio esencial en la programaci&#xF3;n orientada a objetos, especialmente en Java. Al implementar estrategias como el uso de interfaces, la inyecci&#xF3;n de dependencias y patrones de dise&#xF1;o como el observador, los desarrolladores pueden crear aplicaciones m&#xE1;s mantenibles, escalables y f&#xE1;ciles de probar. Adoptar estos enfoques no solo mejorar&#xE1; la calidad de tu c&#xF3;digo, sino que tambi&#xE9;n optimizar&#xE1; el proceso de desarrollo a largo plazo.</p>]]></content:encoded></item><item><title><![CDATA[Utilizando MongoDB con DockerCompose y MongoExpress]]></title><description><![CDATA[Ejecución de B.D. MongoDB utilizando DockerCompose y gestión de la B.D. desde el navegador utilizando MongoExpress.]]></description><link>http://www.atopecode.net/mongodb-con-docker-compose-y-mongo-express/</link><guid isPermaLink="false">652a8e435de8f97635877d51</guid><category><![CDATA[Backend]]></category><category><![CDATA[MongoDB]]></category><category><![CDATA[Docker]]></category><dc:creator><![CDATA[Silverio Martínez García]]></dc:creator><pubDate>Sat, 14 Oct 2023 14:23:32 GMT</pubDate><media:content url="http://www.atopecode.net/content/images/2023/10/MongoDB-unsplash----1000x600.png" medium="image"/><content:encoded><![CDATA[<img src="http://www.atopecode.net/content/images/2023/10/MongoDB-unsplash----1000x600.png" alt="Utilizando MongoDB con DockerCompose y MongoExpress"><p></p><!--kg-card-begin: markdown--><p>En este art&#xED;culo vamos a ver como levantar un servidor de B.D. MongoDB junto con otro servicio llamado &apos;Mongo-Express&apos; que nos permitir&#xE1; acceder a la B.D. mediante una UI desde el navegador web.</p>
<p>Despu&#xE9;s de haber visto varias opciones para utilizar alg&#xFA;n cliente UI de MongoDB, me he encontrado con que la mayor&#xED;a son de pago o he tenido problemas para instalarlos en Linux.</p>
<p><a href="https://github.com/mongo-express/mongo-express">MongoExpress</a> es de c&#xF3;digo abierto y cumple perfectamente con su cometido, por lo que es una opci&#xF3;n a tener en cuenta cuando desarrollamos con MongoDB.</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="http://www.atopecode.net/content/images/2023/10/mongo-express-001.png" class="kg-image" alt="Utilizando MongoDB con DockerCompose y MongoExpress" loading="lazy" width="2000" height="1933" srcset="http://www.atopecode.net/content/images/size/w600/2023/10/mongo-express-001.png 600w, http://www.atopecode.net/content/images/size/w1000/2023/10/mongo-express-001.png 1000w, http://www.atopecode.net/content/images/size/w1600/2023/10/mongo-express-001.png 1600w, http://www.atopecode.net/content/images/size/w2400/2023/10/mongo-express-001.png 2400w" sizes="(min-width: 720px) 720px"></figure><p></p><!--kg-card-begin: markdown--><p>En el <a href="http://www.atopecode.net/instalar-docker-compose-en-linux-ubuntu/" target="_blank">art&#xED;culo anterior</a> vimos como instalar &apos;docker-componse&apos; en Linux.<br>
Vamos a ver a continuaci&#xF3;n el contenido del archivo &apos;docker-compose.yml&apos; que contrendr&#xE1; el servicio de nuestra B.D. MongoDB y el cliente web MongoExpress.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h4 id="docker-componseyml">docker-componse.yml:</h4>
<!--kg-card-end: markdown-->
        <div class="kg-card kg-file-card kg-file-card-medium">
            <a class="kg-file-card-container" href="http://www.atopecode.net/content/files/2023/10/docker-compose.yml" title="Download" download>
                <div class="kg-file-card-contents">
                    <div class="kg-file-card-title">Docker compose</div>
                    
                    <div class="kg-file-card-metadata">
                        <div class="kg-file-card-filename">docker-compose.yml</div>
                        <div class="kg-file-card-filesize">680 Bytes</div>
                    </div>
                </div>
                <div class="kg-file-card-icon">
                    <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 24 24"><defs><style>.a{fill:none;stroke:currentColor;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5px;}</style></defs><title>download-circle</title><polyline class="a" points="8.25 14.25 12 18 15.75 14.25"/><line class="a" x1="12" y1="6.75" x2="12" y2="18"/><circle class="a" cx="12" cy="12" r="11.25"/></svg>
                </div>
            </a>
        </div>
        <!--kg-card-begin: html--><script src="https://gist.github.com/SilverioMG/28303b452f7972bfddd89ef6eacb91e8.js"></script><!--kg-card-end: html--><p></p><!--kg-card-begin: markdown--><p>Como puedes ver en el archivo .yml el <strong>user</strong> y <strong>password</strong> para nuestra B.D. MongoDB es &apos;root&apos;/&apos;example&apos;. Los puedes cambiar por otros valores pero ten en cuenta que tambi&#xE9;n hay que cambiarlos en la secci&#xF3;n <em>&apos;environment&apos;</em> del servicio <strong>&apos;mongo-express&apos;</strong> para que se pueda conectar correctamente contra la B.D.</p>
<p>Adem&#xE1;s el servicio <strong>&apos;mongo&apos;</strong> tiene 2 <strong>vol&#xFA;menes</strong> definidos de tal forma que el contenido de la B.D. y su configuraci&#xF3;n se guardar&#xE1;n respectivamente en las carpetas <strong>&apos;./mongodb_data&apos;</strong> y <strong>&apos;./mongodb_config&apos;</strong> en el mismo path donde est&#xE1; nuestro archivo <em>&apos;docker-compose.yml&apos;</em>.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h3 id="levantando-nuestros-servicios-con-docker-compose">Levantando nuestros servicios con docker-compose:</h3>
<p>Dentro de la misma carpeta donde hemos copiado el archivo <strong>&apos;docker-compose.yml&apos;</strong> ejecutamos el siguiente comando:<br>
<code>&gt; docker-compose up -d</code></p>
<p>A continuaci&#xF3;n podemos ejecutar el siguiente comando para comprobar que ambos contenedores se est&#xE1;n ejecutando correctamente:<br>
<code>&gt; docker ps</code><br>
<code>&gt; docker-compose ps</code></p>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="http://www.atopecode.net/content/images/2023/10/mongodb-docker-componse-up-2.png" class="kg-image" alt="Utilizando MongoDB con DockerCompose y MongoExpress" loading="lazy" width="1398" height="197" srcset="http://www.atopecode.net/content/images/size/w600/2023/10/mongodb-docker-componse-up-2.png 600w, http://www.atopecode.net/content/images/size/w1000/2023/10/mongodb-docker-componse-up-2.png 1000w, http://www.atopecode.net/content/images/2023/10/mongodb-docker-componse-up-2.png 1398w" sizes="(min-width: 720px) 720px"></figure><!--kg-card-begin: markdown--><p>Cuando queramos detener ambos servicios y destruir sus contenedores podemos escribir (dentro de la misma carpeta del archivo <em>docker-compose.yml</em>):<br>
<code>&gt; docker-compose down</code></p>
<p>Si por alg&#xFA;n motivo nuestros contenedores no se han llegado a levantar y necesitamos consultar los logs para ver que ha sucedido podemos utilizar los comandos:<br>
<code>&gt; docker logs mongo</code><br>
<code>&gt; docker logs mongodb_mongo-express_1</code></p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h3 id="accediendo-a-nuestra-bd-desde-mongoexpress">Accediendo a nuestra B.D. desde MongoExpress:</h3>
<p>Una vez que nuestros servicios est&#xE1;n ejecut&#xE1;ndose correctamente, vamos a acceder a nuestra B.D. por medio de <em>&apos;MongoExpress&apos;</em>.<br>
Para ello abrimos nuestro navegador favorito y accedemos a trav&#xE9;s de la url:<br>
<em><a href="http://127.0.0.1:8081/">http://127.0.0.1:8081/</a></em><br>
o<br>
<em><a href="http://localhost:8081/">http://localhost:8081/</a></em></p>
<p>A gusto del consumidor :)</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="http://www.atopecode.net/content/images/2023/10/mongo-express-001-1.png" class="kg-image" alt="Utilizando MongoDB con DockerCompose y MongoExpress" loading="lazy" width="1915" height="1010" srcset="http://www.atopecode.net/content/images/size/w600/2023/10/mongo-express-001-1.png 600w, http://www.atopecode.net/content/images/size/w1000/2023/10/mongo-express-001-1.png 1000w, http://www.atopecode.net/content/images/size/w1600/2023/10/mongo-express-001-1.png 1600w, http://www.atopecode.net/content/images/2023/10/mongo-express-001-1.png 1915w" sizes="(min-width: 720px) 720px"></figure><!--kg-card-begin: markdown--><p>Desde <strong>&apos;MongoExpress&apos;</strong> podemos crear, eliminar y consultar nuestras Bases de Datos.<br>
Como ves en el ejemplo yo he a&#xF1;adido una nueva B.D. llamada &apos;SharingCoders&apos;.</p>
<p>Haciendo click en la B.D. accedemos a la gesti&#xF3;n de las Colecciones, donde podremos crear, eliminar y consultar el contenido de cada una de ellas.<br>
Dentro de la B.D. &apos;SharingCoders&apos; he a&#xF1;adido una nueva colecci&#xF3;n llamada &apos;Issue&apos;:</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="http://www.atopecode.net/content/images/2023/10/mongo-express-002.png" class="kg-image" alt="Utilizando MongoDB con DockerCompose y MongoExpress" loading="lazy" width="1915" height="1019" srcset="http://www.atopecode.net/content/images/size/w600/2023/10/mongo-express-002.png 600w, http://www.atopecode.net/content/images/size/w1000/2023/10/mongo-express-002.png 1000w, http://www.atopecode.net/content/images/size/w1600/2023/10/mongo-express-002.png 1600w, http://www.atopecode.net/content/images/2023/10/mongo-express-002.png 1915w" sizes="(min-width: 720px) 720px"></figure><!--kg-card-begin: markdown--><p>Haciendo click en cada Colecci&#xF3;n, podremos ver su contenido directamente desde la web o incluyo ejecutar querys para consultarlas m&#xE1;s en detalle.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h3 id="conclusi%C3%B3n">Conclusi&#xF3;n:</h3>
<p>En este art&#xED;culo hemos visto como podemos ejecutar nuestro servidor de B.D. <strong>&apos;MongoDB&apos;</strong> utilizando <strong>Docker/docker-compose</strong> y gestionarlo desde nuestro navegador por medio de <strong>&apos;MongoExpress&apos;</strong>.</p>
<p>Espero que haya sido de utilidad ya que no es f&#xE1;cil encontrar un cliente UI para MongoDB en Linux que cumpla nuestras necesidades.</p>
<p>Un saludo.<br>
A Tope Codear!!!</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h3 id="enlaces-de-relacionados">Enlaces de relacionados:</h3>
<!--kg-card-end: markdown--><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/mongo-express/mongo-express"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - mongo-express/mongo-express: Web-based MongoDB admin interface, written with Node.js and express</div><div class="kg-bookmark-description">Web-based MongoDB admin interface, written with Node.js and express - GitHub - mongo-express/mongo-express: Web-based MongoDB admin interface, written with Node.js and express</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt="Utilizando MongoDB con DockerCompose y MongoExpress"><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">mongo-express</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/12199283a4621fe5c8e009505731b49de3179d3d787635bc5422c8af9e08c6bd/mongo-express/mongo-express" alt="Utilizando MongoDB con DockerCompose y MongoExpress"></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://datademia.es/blog/que-es-mongodb"><div class="kg-bookmark-content"><div class="kg-bookmark-title">&#xBF;Qu&#xE9; es MongoDB? - Datademia</div><div class="kg-bookmark-description">Aprende an&#xE1;lisis de datos, ciencia de datos e ingenier&#xED;a de datos.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://datademia.es/wp-content/uploads/2020/05/cropped-favicon-270x270.png" alt="Utilizando MongoDB con DockerCompose y MongoExpress"><span class="kg-bookmark-author">Datademia</span><span class="kg-bookmark-publisher">Datademia</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://datademia.es/wp-content/uploads/2020/12/que_es_mongodb.png" alt="Utilizando MongoDB con DockerCompose y MongoExpress"></div></a></figure>]]></content:encoded></item><item><title><![CDATA[Instalar docker compose en Linux Ubuntu]]></title><description><![CDATA[Instalación de 'docker-compose' en Linux Ubuntu.]]></description><link>http://www.atopecode.net/instalar-docker-compose-en-linux-ubuntu/</link><guid isPermaLink="false">63d668e05de8f97635877c65</guid><category><![CDATA[Docker]]></category><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Silverio Martínez García]]></dc:creator><pubDate>Sat, 14 Oct 2023 11:12:09 GMT</pubDate><media:content url="http://www.atopecode.net/content/images/2023/10/instalar-docker-compose-en-linux-ubuntu---1000x600-1.png" medium="image"/><content:encoded><![CDATA[<img src="http://www.atopecode.net/content/images/2023/10/instalar-docker-compose-en-linux-ubuntu---1000x600-1.png" alt="Instalar docker compose en Linux Ubuntu"><p></p><!--kg-card-begin: markdown--><p>En el <a href="http://www.atopecode.net/docker-instalacion-en-linux-ubuntu/" target="_blank">post anterior</a> vimos como instalar Docker en linux.</p>
<p>En este art&#xED;culo vamos a indicar los pasos para instalar &apos;docker-componse&apos;.<br>
Docker-compose es una herramienta que utiliza Docker y nos permite automatizar y manejar de forma simple la creaci&#xF3;n y ejecuci&#xF3;n de nuestros contenedores.</p>
<p>Nos ahorra tener que utilizar los comandos &apos;docker build ... &apos;, &apos;docker run ...&apos;,<br>
Podemos definir en &#xFA;nico archivo .yml la configuraci&#xF3;n de cada contenedor (imagen, puertos, redes, vol&#xFA;menes...) y con un solo comando se crear&#xE1;n y ejecutar&#xE1;n todos los contenedores con la configuraci&#xF3;n especificada.</p>
<p>Es realmente &#xFA;til, sobre todo en el caso de que necesitemos levantar m&#xE1;s de 1 contenedor a la vez en nuestra m&#xE1;quina. Por ejemplo, nuestra Api REST en SpringBoot y una B.D. PostgreSQL, en este caso necesitar&#xED;amos gestionar la creaci&#xF3;n de ambos contenedores con su correspondiente configuraci&#xF3;n y ejecutar los comandos docker necesarios de forma manual cada vez que que necesitemos crear/ejecutar cada contenedor.</p>
<p>Con docker-compose nos ahorramos todo este trabajo, definimos la construcci&#xF3;n de cada contenedor en el archivo <strong>&apos;docker-compose.yml&apos;</strong> una &#xFA;nica vez y ejecutando en la misma carpeta el comando:<br>
<code>&gt; docker-compose up</code></p>
<p>se crear&#xE1;n y levantar&#xE1;n los contenedores que hemos definido previamente en el archivo .yml.</p>
<p>Para detener (y destruir) los contenedores en ejecuci&#xF3;n, debemos escribir en la misma carpeta donde est&#xE1; nuestro archivo &apos;&apos;docker-compose-yml&apos;:<br>
<code>&gt; docker-compose down </code></p>
<p>As&#xED; de f&#xE1;cil y sencillo.<br>
Este no es un art&#xED;culo para aprender a utilizar docker-compose, solo para ver como podemos instalarlo en Linux, as&#xED; que vamos al l&#xED;o:</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h3 id="instalaci%C3%B3n-en-linux-v1292">Instalaci&#xF3;n en Linux (v1.29.2):</h3>
<p>Ejecutar desde la terminal los siguientes comandos:</p>
<p><code>&gt; sudo curl -L &quot;https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)&quot; -o /usr/local/bin/docker-compose</code></p>
<p><code>&gt; sudo chmod +x /usr/local/bin/docker-compose</code></p>
<p><code>&gt; sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose </code></p>
<p>Para comprobar que se ha instalado correctamente:<br>
<code>&gt; docker-compose</code><br>
Se mostrar&#xE1; el siguiente mensaje:<br>
<em>Define and run multi-container applications with Docker.</em><br>
A continuaci&#xF3;n se muestra la ayuda de como se debe utilizar el comando y todas las opciones disponibles.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h3 id="enlaces-relacionados">Enlaces relacionados:</h3>
<p>Si quieres instalar la &#xFA;ltima versi&#xF3;n a d&#xED;a de hoy (v2.20.3):</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://docs.docker.com/compose/install/standalone/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Install Compose standalone</div><div class="kg-bookmark-description">How to install Docker Compose - Other Scenarios</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://docs.docker.com/assets/favicons/docs@2x.ico" alt="Instalar docker compose en Linux Ubuntu"><span class="kg-bookmark-author">Docker Documentation</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://docs.docker.com/assets/favicons/docs@2x.ico" alt="Instalar docker compose en Linux Ubuntu"></div></a></figure><p>Para aprender un poco m&#xE1;s sobre el uso de &apos;docker-compose&apos;:</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://anderfernandez.com/blog/tutorial-docker-compose/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Tutorial Docker Compose - Ander Fern&#xE1;ndez</div><div class="kg-bookmark-description">En este post te explico, paso a paso, los principales componentes de Docker Compose, c&#xF3;mo funcionan y c&#xF3;mo usarlos.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://anderfernandez.com/wp-content/uploads/2019/07/cropped-faviconajustado-270x270.png" alt="Instalar docker compose en Linux Ubuntu"><span class="kg-bookmark-author">Ander Fern&#xE1;ndez</span><span class="kg-bookmark-publisher">Ander Fern&#xE1;ndez Jauregui</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://anderfernandez.com/wp-content/uploads/2023/03/Docker-Compose.png" alt="Instalar docker compose en Linux Ubuntu"></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.adictosaltrabajo.com/2022/12/19/despliegue-de-aplicaciones-con-docker-compose/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Despliegue de aplicaciones con Docker-Compose - Adictos al trabajo</div><div class="kg-bookmark-description">Vamos a aprender a desplegar nuestros proyectos en contenedores Docker de manera sencilla y r&#xE1;pida, utilizando docker-compose.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.adictosaltrabajo.com/wp-content/uploads/2018/07/cropped-favicon-1-270x270.png" alt="Instalar docker compose en Linux Ubuntu"><span class="kg-bookmark-author">Adictos al trabajo</span><span class="kg-bookmark-publisher">Nicolae Alexandru Molnar</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://www.adictosaltrabajo.com/wp-content/uploads/2022/12/belgium-1601918_1920.jpg" alt="Instalar docker compose en Linux Ubuntu"></div></a></figure>]]></content:encoded></item><item><title><![CDATA[Docker - Instalación en Linux Ubuntu]]></title><description><![CDATA[Instalación de Docker en Linux Ubuntu.
Pasos para instalación y configuración.]]></description><link>http://www.atopecode.net/docker-instalacion-en-linux-ubuntu/</link><guid isPermaLink="false">63b9b2695de8f97635877a98</guid><category><![CDATA[Docker]]></category><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Silverio Martínez García]]></dc:creator><pubDate>Sat, 07 Jan 2023 23:09:05 GMT</pubDate><media:content url="http://www.atopecode.net/content/images/2023/01/docker-parte-1-instalacion-en-linux-ubuntu_1000X600.jpg" medium="image"/><content:encoded><![CDATA[<img src="http://www.atopecode.net/content/images/2023/01/docker-parte-1-instalacion-en-linux-ubuntu_1000X600.jpg" alt="Docker - Instalaci&#xF3;n en Linux Ubuntu"><p></p><!--kg-card-begin: markdown--><p>A d&#xED;a de hoy si eres Desarrollador Backend, Docker es una herramienta casi imprescindible.</p>
<p>En este art&#xED;culo vamos a explicar como se instalar Docker en Linux Ubuntu.</p>
<p>Se da por supuesto que ya sabes lo <a href="https://dev.to/prox_sea/que-es-docker-y-para-que-sirve-explicacion-5h2n" target="blank">que es Docker</a> y tienes conocimientos b&#xE1;sicos de su funcionamiento (im&#xE1;genes, dockerfile, contenedores...).</p>
<!--kg-card-end: markdown--><p></p><p></p><!--kg-card-begin: markdown--><h3 id="instalaci%C3%B3n">Instalaci&#xF3;n:</h3>
<p>1: Primero vamos a actualizar la lista de paquetes de nuestro S.O.:</p>
<pre><code>&gt; sudo apt update
</code></pre>
<br>
<p>2: Instalamos paquetes necesarios para poder utilizar el comando &apos;apt&apos; a trav&#xE9;s de una conexi&#xF3;n Https:</p>
<pre><code>&gt; sudo apt install apt-transport-https ca-certificates curl software-properties-common
</code></pre>
<br>
<p>3: A&#xF1;adimos la clave de GPG para el repositorio oficial de Docker en nuestro sistema:</p>
<pre><code>&gt; curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
</code></pre>
<br>
<p>4: A&#xF1;adimos el repositorio de Docker a las fuentes de &apos;apt&apos;:</p>
<pre><code>&gt; sudo add-apt-repository &quot;deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable&quot;
</code></pre>
<br>
<p>5: Volvemos a actualizar la lista de paquetes de nuestro S.O. despu&#xE9;s de haber a&#xF1;adido el repositorio de Docker:</p>
<pre><code>&gt; sudo apt update
</code></pre>
<br>
<p>6: Este comando es opcional pero podemos ejecutarlo para asegurarnos que la versi&#xF3;n candidata para instalar, es la del repositorio de Docker y no del repositorio predeterminado de Ubuntu.</p>
<pre><code>&gt; apt-cache policy docker-ce
</code></pre>
<p>Despu&#xE9;s de ejecutar el comando se muestra la versi&#xF3;n de &apos;docker-ce&apos; Candidate y se puede comprobar en la lista que la url para esa versi&#xF3;n es la de: <em><a href="https://download.docker.com/linux/ubuntu">https://download.docker.com/linux/ubuntu</a></em>.<br>
<br></p>
<p>7: Por fin podemos instalar Docker:</p>
<pre><code>&gt; sudo apt install docker-ce
</code></pre>
<br>
<p>8: Podemos comprobar que la instalaci&#xF3;n a finalizado correctamente y que el servicio (daemon) de Docker se est&#xE1; ejecutando en nuestro sistema con el comando:</p>
<pre><code>&gt; sudo systemctl status docker
</code></pre>
<p>Si todo ha salido correctamente, se indicar&#xE1; que el servicio <em>&apos;docker.service&apos;</em> tiene estado <em>&apos;Active: active&apos;</em>.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h3 id="configurar-docker-para-que-se-ejecute-al-arrancar-el-sistema">Configurar Docker para que se ejecute al arrancar el Sistema</h3>
<p>Para hacer que el servicio de Docker se ejecute autom&#xE1;ticamente cada vez que se inicia nuestro S.O. Linux, debemos utilizar los siguientes comandos:</p>
<pre><code>&gt; sudo systemctl enable docker.service
&gt; sudo systemctl enable containerd.service
</code></pre>
<br>
<p>Si queremos deshacer los cambios anteriores y preferimos arrancar el servicio de Docker de forma manual, debemos utilizar:</p>
<pre><code>&gt; sudo systemctl disable docker.service
&gt; sudo systemctl disable containerd.service
</code></pre>
<p>Y para arrancar el servicio de docker de forma manual:</p>
<pre><code>&gt; sudo systemctl start docker
&gt; sudo systemctl status docker
</code></pre>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h3 id="utilizar-docker-como-superusuario">Utilizar docker como SuperUsuario:</h3>
<p>Por defecto, el comando docker solo puede ser ejecutado por el usuario <strong>root</strong> o un usuario del grupo <strong>docker</strong>, (que se crea autom&#xE1;ticamente durante el proceso de instalaci&#xF3;n de Docker).<br>
Si se intenta ejecutar <em>docker</em> sin el comando <em>sudo</em> o sin formar parte del grupo <em>docker</em>, se obtiene el siguiente error:</p>
<pre><code>Output
docker: Cannot connect to the Docker daemon. Is the docker daemon running on this host?.
See &apos;docker run --help&apos;.
</code></pre>
<br>
<p>Para evitar el uso de <strong>sudo</strong> cada vez que utilicemos el comando <strong>docker</strong> vamos a seguir los siguientes pasos:<br>
<br><br>
1: Creamos el grupo &apos;docker&apos;, en teor&#xED;a ya deber&#xED;a de estar creado despu&#xE9;s de la instalaci&#xF3;n de Docker, pero por si acaso, ejecutamos:</p>
<pre><code>&gt; sudo groupadd docker
</code></pre>
<br>
<p>2: A&#xF1;adimos nuestro usuario al grupo <strong>docker</strong>:</p>
<pre><code>&gt; sudo usermod -aG docker $USER
</code></pre>
<p>O si estamos logueados con un usuario en concreto y queremos a&#xF1;adir otro usuario (username) al grupo docker:</p>
<pre><code>&gt; sudo usermod -aG docker username
</code></pre>
<br>
<p>3: Activamos los cambios realizados en el grupo docker:</p>
<pre><code>&gt; newgrp docker
&gt; su - ${USER}
</code></pre>
<br>
<p>4: Para comprobar que podemos ejecutar el comando <strong>docker</strong> de forma correcta escribimos:</p>
<pre><code>&gt; docker run hello-world
</code></pre>
<br>
<p>Con este comando, docker se baja una imagen de test y crea un contenedor a partir de ella. Arranca el contenedor mostrando los correspondientes mensajes y a continuaci&#xF3;n se detiene.</p>
<p>Si depu&#xE9;s de ejecutar el comando anterior aparece el siguiente mensaje:</p>
<pre><code>WARNING: Error loading config file: /home/user/.docker/config.json -
stat /home/user/.docker/config.json: permission denied
</code></pre>
<p>Puede ser debido a que en alg&#xFA;n momento se ejecut&#xF3; el comando <strong>docker</strong> con <strong>sudo</strong> antes de a&#xF1;adir el usuario al grupo <strong>docker</strong>.<br>
Para solucionarlo hay que modificar el propietario y los permisos de la carpeta <em>&apos;~/.docker/&apos;</em> para que nuestro usuario tenga acceso:</p>
<pre><code>&gt; sudo chown &quot;$USER&quot;:&quot;$USER&quot; /home/&quot;$USER&quot;/.docker -R
&gt; sudo chmod g+rwx &quot;$HOME/.docker&quot; -R
</code></pre>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h3 id="conclusi%C3%B3n">Conclusi&#xF3;n:</h3>
<p>En este art&#xED;culo hemos visto como instalar Docker en Linux Ubuntu y como configurar nuestro usuario para poder ejecutar el comando <strong>&apos;docker&apos;</strong> sin tener que utilizar <strong>&apos;sudo&apos;</strong>.</p>
<p>Espero que haya sido de utilidad y que disfrutes de las comodidades de <strong>Docker</strong> a partir de ahora.</p>
<p>A tope codear!!!</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h3 id="enlaces-relacionados">Enlaces relacionados:</h3>
<!--kg-card-end: markdown--><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-20-04-es"><div class="kg-bookmark-content"><div class="kg-bookmark-title">C&#xF3;mo instalar y usar Docker en Ubuntu 20.04 | DigitalOcean</div><div class="kg-bookmark-description">Docker es una aplicaci&#xF3;n que simplifica el proceso de administraci&#xF3;n de procesos de aplicaci&#xF3;n en contenedores. Los contenedores le permiten ejecutar sus apl&#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.digitalocean.com/_next/static/media/android-chrome-512x512.5f2e6221.png" alt="Docker - Instalaci&#xF3;n en Linux Ubuntu"><span class="kg-bookmark-author">DigitalOcean</span><span class="kg-bookmark-publisher">Brian Hogan</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://www.digitalocean.com/_next/static/media/intro-to-cloud.d49bc5f7.jpeg" alt="Docker - Instalaci&#xF3;n en Linux Ubuntu"></div></a></figure><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://docs.docker.com/engine/install/linux-postinstall/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Docker Engine post-installation steps</div><div class="kg-bookmark-description">Optional post-installation steps for Linux</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://docs.docker.com/assets/favicons/docs@2x.ico" alt="Docker - Instalaci&#xF3;n en Linux Ubuntu"><span class="kg-bookmark-author">Docker Documentation</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://docs.docker.com/assets/favicons/docs@2x.ico" alt="Docker - Instalaci&#xF3;n en Linux Ubuntu"></div></a></figure><p></p>]]></content:encoded></item><item><title><![CDATA[PisthorIoc - Contenedor para Inyección de Dependencias Java.]]></title><description><![CDATA[Uso de contenedor de dependencias
PisthorIoc ideal para microframeworks.]]></description><link>http://www.atopecode.net/pisthorioc-contenedor-para-inyeccion-de-dependencias-java/</link><guid isPermaLink="false">62191e8b4cbb5a5adf1ec0af</guid><category><![CDATA[Java]]></category><category><![CDATA[Backend]]></category><category><![CDATA[OpenSource]]></category><category><![CDATA[Solid]]></category><dc:creator><![CDATA[Silverio Martínez García]]></dc:creator><pubDate>Sun, 27 Feb 2022 14:16:57 GMT</pubDate><media:content url="http://www.atopecode.net/content/images/2022/02/pisthor---color---1280x640.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="http://www.atopecode.net/content/images/2022/02/pisthor---color---1280x640.png" alt="PisthorIoc - Contenedor para Inyecci&#xF3;n de Dependencias Java."><p>Llevo unos d&#xED;as con un peque&#xF1;o proyecto al que he llamado <strong><a href="https://github.com/SilverioMG/PisthorIoc" target="_blank">PisthorIoc</a></strong>.<br>
El nombre suena un poco a friki porque es la ombinaci&#xF3;n de las palabras <em>Pist&#xF3;n</em> + <em>Thor</em> + &quot;Ioc&quot;.</p>
<p>Lo empec&#xE9; como reto para hacer un Contenedor para Inyecci&#xF3;n de Dependencias y tambi&#xE9;n porque empec&#xE9; a trastear con el microframework <a href="https://javalin.io/" target="_blank">Javalin</a> y echaba de menos usar inyecci&#xF3;n de dependencias.</p>
<p>Existen varias librer&#xED;as de contenedores para inyecci&#xF3;n de dependencias pero ninguna me convenci&#xF3; por lo engorroso que era utilizarlas. Me apetec&#xED;a utilizar una librer&#xED;a que no hiciese uso de la reflexi&#xF3;n, ya que va en contra de la filosof&#xED;a de Javalin y otros microframeworks.</p>
<p>Por eso decid&#xED; intentar hacer uno por mi mismo.</p>
<p>Con <a href="https://github.com/SilverioMG/PisthorIoc" target="_blank">PisthorIoc</a> lo que he tratado de hacer es un contenedor sencillo de utilizar pero potente y que sea lo m&#xE1;s completo y fiable posible.</p>
<p>El proyecto puedes encontrarlo en GitHub:<br>
<a href="https://github.com/SilverioMG/PisthorIoc" target="_blank">https://github.com/SilverioMG/PisthorIoc</a></p>
<p>Como lo implement&#xE9; usando Java11 y haciendo uso de los m&#xF3;dulos de java, decid&#xED; hacer otro proyecto a parte para los tests y as&#xED; de paso comprobar tambi&#xE9;n que la importaci&#xF3;n de m&#xF3;dulos funciona correctamente:<br>
<a href="https://github.com/SilverioMG/PisthorIoc_Test" target="_blank">https://github.com/SilverioMG/PisthorIoc_Test</a></p>
<p>Espero que sea de utilidad para alguien, sobre todo si est&#xE1;s usando alg&#xFA;n microframework como <a href="https://javalin.io/" target="_blank">Javalin</a> o <a href="https://sparkjava.com/" target="_blank">Spark</a>.</p>
<p>Sino tambi&#xE9;n puedes echarle un vistazo al c&#xF3;digo fuente y ver como se puede implementar un contendor de dependencias de manera sencilla :)</p>
<!--kg-card-end: markdown--><p></p><hr><p></p><!--kg-card-begin: markdown--><h1 id="pisthorioccontenedordedependenciassencilloyrpido">PisthorIoc - Contenedor de Dependencias sencillo y r&#xE1;pido</h1>
<p>PisthorIoc es un Contenedor para Inyecci&#xF3;n de Dependencias para proyectos Java.</p>
<ul>
<li>Es simple de utilizar y ligero.</li>
<li>No utiliza reflexi&#xF3;n. No hay magia, solo c&#xF3;digo.</li>
<li>ThreadSafe.</li>
<li>Inyecci&#xF3;n de dependencias v&#xED;a constructor.</li>
<li>FluentApi.</li>
<li>Permite registrar dependencias de tipo <em>Singleton</em> y <em>Prototype</em>.</li>
<li>Es ideal para utilizar en microframeworks como <a href="https://javalin.io/" target="_blank">Javalin</a> o <a href="https://sparkjava.com/" target="_blank">Spark</a>.</li>
<li>Cada dependencia se registra con un identificador &#xFA;nico de tipo &apos;String&apos; que ser&#xE1; el mismo que se utilice a la hora de resolver/inyectar cada dependencia.</li>
<li>Detecta errores comunes como intentar utilizar dependencias no registradas, registro de dependencias c&#xED;clicas, error en conversi&#xF3;n de tipos al inyectar dependencias, aviso al sobreescribir el registro de alguna dependencia...</li>
<li>Permite resolver las dependencias a medida que se van necesitando en la ejecuci&#xF3;n de nuestro c&#xF3;digo (lazy load) o realizar una carga completa cuando inicia nuestro proyecto de todas las dependencias registradas (recomendado).</li>
<li>A la hora de hacer <em>tests</em> se puede sobreescribir directamente el registro de cualquier dependencia (servicio, repositorio...) para sustituirla por un <em>mock</em> o la correspondiente implementaci&#xF3;n de pruebas.</li>
</ul>
<pre><code>final String REPOSITORY = &quot;repository&quot;;
final String SERVICE1 = &quot;service1&quot;;
final String SERVICE2 = &quot;service2&quot;;
final String CONTROLLER = &quot;controller&quot;;

Logger logger = LoggerFactory.getLogger(IocContainer.class);
IocContainer iocContainer = IocContainerFactory.singleton()
	.setLogger(logger);
	
iocContainer
    .register(
        REPOSITORY,  
        (dr) -&gt; new Repository())
    .register(
        SERVICE1,
        (dr) -&gt; new Service1(dr.resolve(REPOSITORY , IRepository.class)))
    .register(
        SERVICE2,
        (dr) -&gt; new Service2(dr.resolve(REPOSITORY , IRepository.class)))
    .register(
        CONTROLLER,
        (dr) -&gt; new Controller(
            dr.resolve(SERVICE1, IService.class),
            dr.resolve(SERVICE2, IService.class)))
    .loadContent();
	
Controller controller = iocContainer.resolve(CONTROLLER, Controller.class);
</code></pre>
<h2 id="importarlibrerapisthoriocentuproyecto">Importar librer&#xED;a PisthorIoc en tu proyecto:</h2>
<h5 id="compilarlibreraenlocalconmaven">Compilar librer&#xED;a en local con Maven</h5>
<p>Por el momento la librer&#xED;a <em>PisthorIoc</em> no est&#xE1; subida a ning&#xFA;n repositorio p&#xFA;blico como <em>MavenCentral</em> por lo que tendr&#xE1;s que compilar la librer&#xED;a e instalarla en su repositorio local.</p>
<p>Primero debes descargar la &#xFA;ltima versi&#xF3;n del proyecto <a href="https://github.com/silveriomg/pisthorioc/releases/latest" target="_blank">PisthorIoc</a>.<br>
A continuaci&#xF3;n, ejecuta el siguiente comando maven dentro del directorio ra&#xED;z del proyecto:</p>
<pre><code>mvn clean install
</code></pre>
<p>El proyecto se compilar&#xE1; y guardar&#xE1; el archivo <em>.jar</em> generado en tu directorio maven local <em>.m2</em>.</p>
<h5 id="aadirlibreraentuproyecto">A&#xF1;adir librer&#xED;a en tu proyecto</h5>
<p>Si tu proyecto utiliza Maven:</p>
<pre><code>&lt;dependency&gt;  
    &lt;groupId&gt;net.atopecode&lt;/groupId&gt;  
    &lt;artifactId&gt;pisthorioc&lt;/artifactId&gt;  
    &lt;version&gt;1.0.0&lt;/version&gt;  
&lt;/dependency&gt;
</code></pre>
<p>Si tu proyecto utiliza  Gradle:</p>
<pre><code>implementation &quot;net.atopecode:pisthorioc:1.0.0&quot;
</code></pre>
<h5 id="usodeloggerparamostrarmensajes">Uso de Logger para mostrar mensajes</h5>
<p>PisthorIoc permite utilizar logger (o no) seg&#xFA;n tus necesidades. En caso de no utilizar logger, la librer&#xED;a funciona igualmente pero no muestra advertencias ni mensajes de error (pero seguir&#xE1; lanzando las correspondientes <em>Exceptions</em> en caso de error).</p>
<p>Es aconsejable utilizar siempre un logger para ver los mensajes y detectar posibles errores a medida que se registran y resuelven las dependencias.</p>
<p>PisthorIoc necesita un logger que implemente la interfaz <em>slf4j</em>.<br>
En este ejemplo utilizaremos el logger <em>logback</em> para que nuestro contenedor de dependencias haga uso de &#xE9;l.<br>
A&#xF1;ade las siguientes dependencias en tu proyecto:</p>
<p>Si tu proyecto utiliza Maven:</p>
<pre><code>&lt;dependency&gt;  
    &lt;groupId&gt;ch.qos.logback&lt;/groupId&gt;  
    &lt;artifactId&gt;logback-classic&lt;/artifactId&gt;  
    &lt;version&gt;1.2.10&lt;/version&gt;  
&lt;/dependency&gt;  
&lt;dependency&gt;  
    &lt;groupId&gt;ch.qos.logback&lt;/groupId&gt;  
    &lt;artifactId&gt;logback-core&lt;/artifactId&gt;  
    &lt;version&gt;1.2.10&lt;/version&gt;  
&lt;/dependency&gt;
</code></pre>
<p>Si tu proyecto utiliza  Gradle:</p>
<pre><code>implementation &quot;ch.qos.logback:logback-classic:1.2.10&quot;
implementation &quot;ch.qos.logback:logback-core:1.2.10&quot;
</code></pre>
<h5 id="importarmdulojavadelalibrerapisthoriocentuproyecto">Importar m&#xF3;dulo Java de la librer&#xED;a PisthorIoc en tu proyecto</h5>
<p>La librer&#xED;a PisthorIoc est&#xE1; implementada utilizando <em><a href="https://www.oracle.com/es/corporate/features/understanding-java-9-modules.html" target="_blank">m&#xF3;dulos de Java</a></em>.<br>
Si tu proyecto utiliza <em>m&#xF3;dulos de java</em> debes a&#xF1;adir la siguiente l&#xED;nea en tu archivo <em>module-info.java</em>:</p>
<pre><code>requires net.atopecode.pisthorioc.module;
</code></pre>
<p>Si tu proyecto no utiliza <em>m&#xF3;dulos de java</em> no debes hacer nada especial para utilizar PisthorIoc, simplemente importa el paquete <em>net.atopecode.pisthorioc.ioccontainer.</em>* en tu c&#xF3;digo como har&#xED;as normalmente.</p>
<h2 id="utilizandopisthoriocentuproyecto">Utilizando PisthorIoc en tu proyecto:</h2>
<p>El uso del contendor de dependencias <em>PisthorIoc</em> se puede dividir en los siguientes pasos:</p>
<ol>
<li><a href="#1crearcontenedordedependencias">Crear contenedor de dependencias.</a></li>
<li><a href="#2registrartodaslasdependenciasnecesarias">Registrar todas las dependencias necesarias.</a></li>
<li><a href="#3cargaautomticadelcontenedorcontodaslasdependenciasregistradas">Carga autom&#xE1;tica del contenedor con todas las dependencias registradas.</a></li>
<li><a href="#4usodelcontenedorpararesolverdependencias">Uso del contenedor para resolver dependencias.</a></li>
</ol>
<h5 id="1crearcontenedordedependencias">1. Crear contenedor de dependencias</h5>
<h6 id="crearuncontenedorcomosingletonelmismoobjetoenmemoriadurantetodalaejecucindelprograma">Crear un contenedor como <em>Singleton</em> (el mismo objeto en memoria durante toda la ejecuci&#xF3;n del programa):</h6>
<pre><code>Logger logger = LoggerFactory.getLogger(IocContainer.class);
IocContainer container = IocContainerFactory.singleton()
    .setLogger(logger);
</code></pre>
<p>El m&#xE9;todo static <em>&apos;IocContainerFactory.singleton()&apos;</em> siempre nos devolver&#xE1; la misma instancia (mismo objeto) de nuestro contenedor de dependencias.<br>
Por lo tanto, siempre podremos acceder a nuestro contenedor singleton desde cualquier parte de nuestro c&#xF3;digo utilizando el m&#xE9;todo static <em>&apos;IocContainerFactory.singleton()&apos;</em> sin necesidad de guardar el objeto contenedor en un campo o ir pas&#xE1;ndolo como par&#xE1;metro de una parte del c&#xF3;digo a otra:</p>
<pre><code>IocContainerFactory.singleton()
    .register(...);
	
IocContainerFactory.singleton()
    .resolve(...);
</code></pre>
<h6 id="crearuncontenedornuevosiesnecesariounnuevoobjetoenmemoria">Crear un contenedor nuevo si es necesario (un nuevo objeto en memoria):</h6>
<pre><code>Logger logger = LoggerFactory.getLogger(IocContainer.class);
IocContainer container = IocContainerFactory.newInstance()
    .setLogger(logger);
</code></pre>
<p>El m&#xE9;todo static <em>&apos;IocContainerFactory.newInstance()&apos;</em> siempre nos devolver&#xE1; una nueva instancia (distinto objeto) de un contenedor de dependencias.</p>
<h6 id="usandounlogger">Usando un Logger:</h6>
<p>En los ejemplos anteriores hemos creado siempre nuestro contenedor asign&#xE1;ndole un logger.<br>
Solo se permite asignar un logger a cada IocContainer una vez, es decir, si un IocContainer ya tiene un logger asignado y se intenta asignar de nuevo un logger, se produce una Exception.</p>
<p>Se puede comprobar si un contenedor tiene un logger ya asignado en cualquier momento:</p>
<pre><code>IocContainer container = IocContainerFactory.singleton()
container.hasLogger(); //Devuelve &apos;false&apos;.
container.setLogger(logger);
container.hasLogger(); //Devuelve &apos;true&apos;.

//Se produce una &apos;Exception&apos; porque ya se ha asignado previamente un logger al contenedor.
container.setLogger(logger); 
</code></pre>
<h6 id="sinusarlogger">Sin usar Logger:</h6>
<pre><code>IocContainer container1 = IocContainerFactory.singleton();
IocContainer container2 = IocContainerFactory.newInstance();
</code></pre>
<p>Al no asignar <em>logger</em> al contenedor, su funcionamiento ser&#xE1; el mismo, pero no se mostrar&#xE1;n mensajes de advertencia o de errores (pero se siguen lanzando las correspondientes Exceptions).</p>
<h5 id="2registrartodaslasdependenciasnecesarias">2. Registrar todas las dependencias necesarias</h5>
<p><em>PisthorIoc</em> utiliza inyecci&#xF3;n de dependencias v&#xED;a constructor. Por lo que debe dise&#xF1;ar todos los objetos que vaya a utilizar como dependencias (controladores, servicios, repositorios...) para que sus constructores reciban las dependencias necesarias en cada momento.</p>
<p>Para nuestro ejemplo vamos a definir las siguientes clases (con sus correspondientes interfaces) que actuar&#xE1;n como dependencias dentro de nuestro contenedor y que se inyectar&#xE1;n unas en otras usando sus constructores:</p>
<pre><code>public interface IRepository {
    public void methodRepository();
}
</code></pre>
<pre><code>public interface IService {
    public void methodService();
}
</code></pre>
<pre><code>public class Repository implements IRepository {
    public Repository(){
    }

    @Override
    public void methodRepository(){
    }
}
</code></pre>
<pre><code>public class Service1 implements IService {
    private final IRepository repository;

    public Service1(IRepository repository){
        this.repository = repository;
    }
 
    @Override
    public void methodService(){
    }
}
</code></pre>
<pre><code>public class Service2 implements IService {
    private final IRepository repository;

    public Service2(IRepository repository){
        this.repository = repository;
    }
 
    @Override
    public void methodService(){
    }
}
</code></pre>
<pre><code>public class Controller {
    private final IService service1;
    private final IService service2;
	
    public Controller(IService service1, IService service2){
        this.service1 = service1;
        this.service2 = service2;
    }
    
    ...
}
</code></pre>
<p>Una vez definidas las clases para nuestras dependencias vamos a registrarlas dentro del contenedor de dependencias indicando como debe ser la construcci&#xF3;n de cada una de ellas e inyectando las dependencias necesarias en cada caso.</p>
<p><em>Registrar</em> una dependencia en <em>PisthorIoc</em> consiste en indicarle al contenedor como debe crearse un objeto y las dependencias que necesita para ello. Cuando se necesite <em>resolver</em> (recuperar) un objeto, el contenedor sabr&#xE1; como debe crearlo inyectando las dependencias que necesita en su constructor.</p>
<pre><code>final String REPOSITORY = &quot;repository&quot;;
final String SERVICE1 = &quot;service1&quot;;
final String SERVICE2 = &quot;service2&quot;;
final String CONTROLLER = &quot;controller&quot;;

IocContainerFactory.singleton()
    .register(  
        REPOSITORY ,  
        (dr) -&gt; new Repository())
    .register(  
        SERVICE1,  
        (dr) -&gt; new Service1(dr.resolve(REPOSITORY , IRepository.class)))
    .register(  
        SERVICE2,  
        (dr) -&gt; new Service2(dr.resolve(REPOSITORY , IRepository.class)))
    .register(  
        CONTROLLER,  
        (dr) -&gt; new Controller(
            dr.resolve(SERVICE1, IService.class),
            dr.resolve(SERVICE2, IService.class)));
</code></pre>
<h5 id="3cargaautomticadelcontenedorcontodaslasdependenciasregistradas">3. Carga autom&#xE1;tica del contenedor con todas las dependencias registradas</h5>
<p>Partiendo del apartado anterior, ahora mismo en nuestro contenedor est&#xE1;n registradas todas las dependencias que necesitamos, pero a&#xFA;n no se ha creado ning&#xFA;n objeto. El contenedor simplemente tiene las instrucciones necesarias para crear los objetos y sus dependencias cuando sea necesario recuperarlas (resolverlas).</p>
<p>PisthorIoc utiliza un enfoque <em>Lazy Load</em> por defecto para resolver las dependencias.<br>
Cada vez que se intente resolver (recuperar) una dependencia para utilizarla en nuestro c&#xF3;digo, en ese justo momento el contenedor intentar&#xE1; crear el objeto y todas sus dependencias necesarias.</p>
<p>Utilizando el enfoque <em>Lazy Load</em>, es posible que se produzcan errores a la hora de resolver alguna dependencia debido a que se ha registrado de forma incorrecta (no se ha registrado alguna dependencia necesaria para su construcci&#xF3;n, registro de dependencias c&#xED;clicas...). Con lo cual puede que la inyecci&#xF3;n falle de forma no controlada sin saber justo el momento en el que se producir&#xE1; el error debido a que nuestro programa ya lleva un tiempo ejecut&#xE1;ndose.</p>
<p>Para evitar esto, <strong>es preferible realizar una carga autom&#xE1;tica de todas las dependencias justo al finalizar el registro</strong>.<br>
Con la carga autom&#xE1;tica evitamos que se produzcan errores inesperados, ya que podemos controlar justo el momento en el que se pueden producir.<br>
Lo normal es que el registro de las dependencias y la carga autom&#xE1;tica se hagan al inicio de nuestro programa o servicio.</p>
<p>Realizar carga autom&#xE1;tica despu&#xE9;s del registro de dependencias:</p>
<pre><code>IocContainerFactory.singleton()
    .register(  
        &quot;repository&quot;,  
        (dr) -&gt; new Repository())
    .register(  
        &quot;service1&quot;,  
        (dr) -&gt; new Service1(dr.resolve(&quot;repository&quot;, IRepository.class)))
    .loadContent();
</code></pre>
<p>A&#xF1;adimos el m&#xE9;todo <em>loadContent()</em> justo al finalizar el registro de las dependencias.</p>
<h5 id="4usodelcontenedorpararesolverdependencias">4. Uso del contenedor para resolver dependencias</h5>
<p>Una vez que tenemos nuestras <em>dependencias registradas</em> en el punto 2, pasamos a resolver las dependencias para utilizarlas en nuestro proyecto (independientemente de si hemos decidido realizar la <em>carga autom&#xE1;tica</em> del punto 3 o no).</p>
<p>Obtenemos el objeto correspondiente a la dependencia registrada con el nombre &quot;controller&quot;:</p>
<pre><code>IocContainer container = IocContainerFactory.singleton();
Controller controller = container.resolve(&quot;controller&quot;, Controller.class);
</code></pre>
<p>La primera vez que se intenta resolver la dependencia, a su vez, se crean autom&#xE1;ticamente el resto de dependencias necesarias para inyectarlas en su constructor y finalmente se devuelve el objeto de tipo &apos;Controller&apos; con todas sus dependencias inyectadas.</p>
<p>Al estar registrado como <em>Singleton</em>, la pr&#xF3;xima vez que se intente resolver la dependencia de tipo &quot;controller&quot;, el contenedor devolver&#xE1; siempre el mismo objeto en vez de crear uno nuevo.</p>
<p>A continuaci&#xF3;n resolvemos el objeto registrado como singleton con el nombre &quot;service1&quot;:</p>
<pre><code>IService service1 = container.resolve(&quot;service1&quot;, IService.class);
</code></pre>
<p>El contenedor nos devuelve el mismo objeto de tipo &apos;Service1&apos; que se cre&#xF3; cuando resolvimos el objeto con nombre &quot;controller&quot;, ya que hizo falta el &apos;Service1&apos; para inyectarlo en el constructor de &apos;Controller&apos; y nuestro contenedor tuvo que crear el objeto en ese momento.</p>
<h2 id="usoavanzado">Uso avanzado:</h2>
<h5 id="lasdependenciassepuedenregistrarcomosingletonpordefectooprototype">Las dependencias se pueden registrar como &apos;Singleton&apos; (por defecto) o &apos;Prototype&apos;:</h5>
<ul>
<li>Singleton: El objeto se crea una vez y siempre que se recupere su valor se devolver&#xE1; la misma instancia (el mismo objeto).</li>
<li>Prototype: Cada vez que se quiera recuperar un objeto, se crea una nueva instancia (nuevo objeto).</li>
</ul>
<p><em>Sino se indica nada, por defecto todos los objetos se registran como dependencias de tipo <em>Singleton</em>.</em></p>
<p>Registrar un objeto como <em>Singleton</em>:</p>
<pre><code>//Por defecto sino se indica nada, se registra el objeto como &apos;Singleton&apos;.
container
    .register(  
        &quot;repository&quot;,  
        (dr) -&gt; new Repository());

//Indicando de forma expl&#xED;cita el registro como &apos;Singleton&apos;.
container
    .register(  
        &quot;repository&quot;,
        DependencyFactory.DependencyType.SINGLETON,
        (dr) -&gt; new Repository());
</code></pre>
<p>Registrar un objeto como <em>Prototype</em>:</p>
<pre><code>//Siempre hay que indicar forma expl&#xED;cita el registro como &apos;Prototype&apos;.
container
    .register(  
        &quot;repository&quot;,
        DependencyFactory.DependencyType.PROTOTYPE,
        (dr) -&gt; new Repository());
</code></pre>
<h5 id="noimportaelordenderegistrodenuestrasdependencias">No importa el orden de registro de nuestras dependencias:</h5>
<p>A la hora de registrar las dependencias en nuestro contenedor no importa el orden en el que lo hagamos.<br>
Lo &#xFA;nico importante es que a la hora de resolver alguna dependencia, esta haya sido previamente registrada adem&#xE1;s de todas las dem&#xE1;s dependencias que puedan ser necesarias para su construcci&#xF3;n.</p>
<p>Por lo tanto podemos registrar las dependencias en cualquier orden:</p>
<pre><code>final String REPOSITORY = &quot;repository&quot;;
final String SERVICE1 = &quot;service1&quot;;
final String SERVICE2 = &quot;service2&quot;;
final String CONTROLLER = &quot;controller&quot;;

IocContainerFactory.singleton()
    .register(  
        CONTROLLER,  
        (dr) -&gt; new Controller(
            dr.resolve(SERVICE1, IService.class),
            dr.resolve(SERVICE2, IService.class)))
    .register(  
        SERVICE2,  
        (dr) -&gt; new Service2(
            dr.resolve(REPOSITORY , IRepository.class)))
    .register(  
        SERVICE1,  
        (dr) -&gt; new Service1(
            dr.resolve(REPOSITORY , IRepository.class)))
    .register(  
        REPOSITORY ,  
        (dr) -&gt; new Repository())
    .loadContent();

Controller controller = IocContainerFactory.singleton()
    .resolve(CONTROLLER, Controller.class);
</code></pre>
<h5 id="mostrarlasdependenciasqueguardaelcontenedor">Mostrar las dependencias que guarda el contenedor:</h5>
<p>La primera vez que se intenta <em>resolver o inyectar</em> una dependencia registrada como <em>singleton</em>, el contenedor crea su correspondiente objeto y lo almacena en su interior.<br>
Para las dependencias registradas como <em>prototype</em> esto no sucede, ya que el contenedor crea un nuevo objeto cada vez que se intentan <em>resolver o inyectar</em> y por lo tanto no las almacena en su interior.</p>
<p>El m&#xE9;todo <em>showContent()</em> hace logging de todas las dependencias registradas como <em>singleton</em> que un contenedor tiene almacenadas. Es necesario que el contenedor tenga un <em>logger</em> asignado.</p>
<pre><code>Logger logger = LoggerFactory.getLogger(IocContainer.class);
IocContainerFactory.singleton()
    .setLogger(logger)
    .showContent();
</code></pre>
<h5 id="sobreescribirelregistrodeunadependencia">Sobreescribir el registro de una dependencia:</h5>
<pre><code>container
    .register(
        &quot;repository&quot;,
        (dr) -&gt; new Repository())
    .register(
        &quot;repository&quot;,
        (dr) -&gt; new RepositoryMock())
    .loadContent();
    
RepositoryMock repository = container.resolver(&quot;repository&quot;, IRepository.class);
</code></pre>
<p>Si registramos 2 o m&#xE1;s veces el mismo objeto (utilizamos el mismo identificador de tipo string) como en el ejemplo anterior que hemos registrado 2 veces una dependencia con el mismo nombre <em>&quot;repository&quot;</em>, no se produce ning&#xFA;n error, pero el contenedor muestra un mensaje de log como aviso (en caso de que hayamos utilizado un logger al crear el contenedor).<br>
La dependencia con el nombre &quot;repository&quot; quedar&#xE1; registrada con el &#xFA;ltimo registro realizado, sobreescribiendo los registros anteriores.</p>
<p><em>Para que el nuevo registro sobreescriba a sus antecesores y se aplique correctamente a la hora de resolver la dependencia, no se debe haber intentado resolver (recuperar) ninguna dependencia del contenedor ni haber realizado la carga autom&#xE1;tica antes del registro.<br>
Ya que una vez que se crea el objeto de dicha dependencia, se guarda dentro del contenedor y este a su vez puede ser una dependencia de otro objeto. Y a&#xFA;n cuando la dependencia puede estar declarada como Prototype y no se guarde dentro del contenedor, siempre puede haber sido inyectada en otra dependencia de tipo Singleton que siempre guardar&#xE1; una versi&#xF3;n del objeto prototype que no corresponder&#xE1; con el nuevo registro.</em></p>
<p>Sobreescribir el registro de un objeto es realmente &#xFA;til para realizar tests.<br>
Podemos utilizar el mismo registro de dependencias de nuestro proyecto y en los tests sobreescribir solo aquellas dependencias que necesitamos mockear o sustituir su comportamiento.</p>
<h5 id="usodelcontenedorparatestingsobreescribiendoelregistrodelasdependencias">Uso del contenedor para Testing (sobreescribiendo el registro de las dependencias):</h5>
<p>Supongamos que en nuestro proyecto vamos a utilizar las siguientes clases y sus correspondientes interfaces:</p>
<pre><code>public interface IEmailService {
    public void sendEmail();
}

public interface ICustomerService {
    public void sendEmailToCustomer();
}

public class EmailService implements IEmailService{
    @Override
    public void sendEmail(){
        //C&#xF3;digo que env&#xED;a un email de verdad.
        ...
    }
}

public class CustomerService implements ICustomerService{
    private final IEmailService emailService;
	
    public CustomerService(IEmailService emailService){
        this.emailService = emailService;
    }
	
    @Override
    public void sendEmailToCustomer(){
        emailService.sendEmail();
    }
}

public class EmailServiceMock implements IEmailService {
    @Override
    public void sendEmail(){
        //No hace nada.
        return;
    }
}
</code></pre>
<p>Utilizamos una clase &apos;IocContext&apos; que inicialice nuestro contenedor y que se encargue de realizar el registro de todas las dependencias necesarias:</p>
<pre><code>public class IocContext {
    public static void init(IocContainer iocContainer, boolean loadContent){
        Logger logger = LoggerFactory.getLogger(IocContainer.class);
        iocContainer
            .setLogger(logger)
            .register(
                &quot;emailService&quot;,
                (dr) -&gt; new EmailService())
            .register(
                &quot;customerService&quot;,
                (dr) -&gt; new CustomerService(dr.resolve(&quot;emailService&quot;, IEmailService.class)));
		
        if(loadContent) iocContainer.loadContent();
    }
}
</code></pre>
<p><em>Es importante remarcar que en la l&#xED;nea donde se registra &apos;customerService&apos;, a la hora de resolver &apos;emailService&apos; para inyectarlo en su constructor, el objeto de tipo &apos;class&apos; que se env&#xED;a como par&#xE1;metro es del tipo de la interfaz &apos;IEmailService&apos;,<br>
no de la clase &apos;EmailService&apos;. Se hace as&#xED; porque a la hora de sobreescribir el registro de &apos;emailService&apos; en nuestro test para utilizar &apos;EmailServiceMock&apos;, cuando el contenedor intente inyectarle a &apos;customerService&apos; la dependencia de &quot;emailService&quot;, esta ser&#xE1; del tipo &apos;EmailServiceMock&apos; y har&#xE1; casting contra la interfaz &apos;IEmailService&apos; por haber usado el par&#xE1;metro correcto &apos;IEmailService.class&apos; (si se usa como par&#xE1;metro &apos;EmailService.class&apos;, se har&#xED;a casting del objeto de tipo &apos;EmailServiceMock&apos; contra el tipo &apos;EmailService&apos; y fallar&#xED;a).</em></p>
<p>Al inicio de nuestro proyecto (no en los tests) lo normal ser&#xED;a utilizar la clase &apos;IocContext&apos; definida anteriormente para registrar todas las dependencias necesarias y tambi&#xE9;n forzaremos la <em>carga autom&#xE1;tica</em> para comprobar que el registro es correcto.<br>
En nuestro proyecto utilizaremos el contenedor de dependencias como Singleton, pero en los tests ser&#xE1; mejor crear una nueva instancia del contenedor por cada ejecuci&#xF3;n.</p>
<pre><code>//Al inicio de nuestro proyecto:
IocContext.init(IocContainerFactory.singleton(), true);
</code></pre>
<p>A la hora de hacer un test necesitamos cambiar el comportamiento de la dependencia &quot;emailService&quot;.<br>
En producci&#xF3;n se utilizar&#xE1; &apos;EmailService&apos; para enviar los emails necesarios, pero a la hora de ejecutar nuestros tests no queremos que env&#xED;e ning&#xFA;n email y utilizaremos &apos;EmailServiceMock&apos; en su lugar.</p>
<p>Al inicio del test sustituiremos el registro de la dependencia para el servicio de email por nuestro mock, as&#xED; a la hora de resolver el servicio de email, el  contenedor devolver&#xE1; siempre nuestro mock.</p>
<p>Es importante remarcar que para que la sobrescritura del registro funcione correctamente, <strong>en el test no se debe realizar la carga autom&#xE1;tica hasta que no se haya registrado nuestro servicio mock.</strong><br>
Adem&#xE1;s, por cada tests que hagamos, es conveniente utilizar una nueva instancia del contenedor y realizar de nuevo el registro.</p>
<pre><code>@Test
public void emailServiceTest(){
    IocContainer iocContainer = IocContainerFactory.newInstance(); //Nueva instancia del contenedor (no singleton).
    IocContext.init(iocContainer, false); //No se realiza la carga autom&#xE1;tica.
	
    //Sobreescribimos el registro de &quot;emailService&quot; para utilizar &apos;EmailServiceMock&apos;.
    iocContainer
        .register(
            &quot;emailService&quot;,
            (dr) -&gt; new EmailServiceMock());
		
    //Realizamos la carga autom&#xE1;tica despu&#xE9;s de sobreescribir el registro de dependencias.
    iocContainer.loadContent();

    ICustomerService customerService = iocContainer.resolve(&quot;customerService&quot;, ICustomerService.class);
    customerService.sendEmailToCustomer(); //No hace nada.
	
    IEmailService emailService = iocContainer.resolve(&quot;emailService&quot;, IEmailService.class);
    assertTrue(emailService instanceof EmailServiceMock);
}
</code></pre>
<h2 id="msejemplosdeusodepisthorioc">M&#xE1;s ejemplos de uso de PisthorIoc</h2>
<p>Para ver m&#xE1;s ejemplos sobre el uso de <em>PisthorIoc</em> puede consultar los tests de la librer&#xED;a en el proyecto <a href="https://github.com/SilverioMG/PisthorIoc_Test" target="_blank">PisthorIoc_Test</a>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Instalar Linux en Windows por medio de WSL2 (Windows Subsystem for Linux).]]></title><description><![CDATA[Utilizar Linux desde Windows por medio de WSL2. 
Olvídate de utilizar arranque dual con Linux instalado en otra partición.]]></description><link>http://www.atopecode.net/instalar-linux-en-windows-por-medio-de-wsl2-windows-subsystem-for-linux/</link><guid isPermaLink="false">612dfee061797f05151c7ef8</guid><category><![CDATA[Backend]]></category><category><![CDATA[Windows]]></category><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Silverio Martínez García]]></dc:creator><pubDate>Wed, 01 Sep 2021 09:04:19 GMT</pubDate><media:content url="http://www.atopecode.net/content/images/2021/09/Instalar-Linux-en-Windows-por-medio-de-WSL2--Windows-Subsystem-for-Linux-_1000x600-2.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="http://www.atopecode.net/content/images/2021/09/Instalar-Linux-en-Windows-por-medio-de-WSL2--Windows-Subsystem-for-Linux-_1000x600-2.png" alt="Instalar Linux en Windows por medio de WSL2 (Windows Subsystem for Linux)."><p>Para todas aquellas personas como en mi caso que tengan instalados en su pc Windows y Linux y ejecutando ambos mediante arranque dual, este art&#xED;culo puede ser de gran inter&#xE9;s.</p>
<p>Con <strong><a href="https://docs.microsoft.com/es-es/windows/wsl/about" target="_blank">WSL2</a></strong> lo que se consigue es poder instalar una imagen Linux en Windows y ejecutarla mediante virtualizaci&#xF3;n.</p>
<p>En mi caso prefiero Windows para programar y Linux para ejecutar y desplegar ya que al fin y al cabo cuando subo alg&#xFA;n servicio a la nube suele ser en una m&#xE1;quina Linux (por precio, seguridad y rendimiento), pero antes siempre hago pruebas en local y me toca reiniciar el pc y arrancar en Linux.</p>
<p>Al utilizar WSL2 en realidad ya no hace falta tener instalado Linux en otra partici&#xF3;n y utilizar el arranque dual.<br>
Simplemente desde Windows se arranca la imagen de Linux y podremos utilizar la terminal para hacer todo lo que deseemos, ya que estaremos trabajando sobre un Linux de verdad.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><!--iframe style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="https://rcm-eu.amazon-adsystem.com/e/cm?ref=qf_sp_asin_til&t=atopecode-21&m=amazon&o=30&p=8&l=as1&IS2=1&asins=1499550022&linkId=0d0cc7dc4f66a9b4e597b3006d72a2d9&bc1=000000&amp;lt1=_blank&fc1=333333&lc1=0066c0&bg1=ffffff&f=ifr"-->
    <!--/iframe--><!--kg-card-end: html--><p></p><!--kg-card-begin: markdown--><h3 id="requisitosparainstalarwsl2enwindows">Requisitos para instalar WSL2 en Windows:</h3>
<ol>
<li>
<p>Tu versi&#xF3;n de Windows debe ser de <strong>64 bits</strong>.</p>
</li>
<li>
<p>Debes de tener <strong>Windows actualizado</strong>. Hay que tener una versi&#xF3;n igual o superior a la <em>1903</em>.<br>
Para ver la versi&#xF3;n de Windows actual presiona la tecla <strong>&apos;Windows + R&apos;</strong> y teclea <strong>&apos;winver&apos;</strong>.</p>
</li>
<li>
<p>Tu procesador debe <strong>soportar virtualizaci&#xF3;n</strong> (en procesadores Intel <strong>VT-x</strong> y en Amd <strong>AMD-V</strong>). Adem&#xE1;s debe de estar habilitada dicha virtualizaci&#xF3;n en la Bios.<br>
Lo normal es que si tu procesador tiene menos de 6 a&#xF1;os ya tenga virtualizaci&#xF3;n y  est&#xE9; activada en la Bios.</p>
</li>
</ol>
<p>Para comprobar si el requisito n&#xBA; 3 es correcto puedes pulsar las teclas <strong>CTRL + May&#xFA;sculas + Esc</strong> y se abrir&#xE1; el <em>Administrador de Tareas de Windows</em>.<br>
En la pesta&#xF1;a <em>Rendimiento</em> seleccionando la CPU se puede ver si la opci&#xF3;n <strong><em>Virtualization</em> est&#xE1; activada</strong>:</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="http://www.atopecode.net/content/images/2021/08/admin_tareas_virtualizacion.png" class="kg-image" alt="Instalar Linux en Windows por medio de WSL2 (Windows Subsystem for Linux)." loading="lazy" width="1104" height="951" srcset="http://www.atopecode.net/content/images/size/w600/2021/08/admin_tareas_virtualizacion.png 600w, http://www.atopecode.net/content/images/size/w1000/2021/08/admin_tareas_virtualizacion.png 1000w, http://www.atopecode.net/content/images/2021/08/admin_tareas_virtualizacion.png 1104w" sizes="(min-width: 720px) 720px"></figure><p></p><!--kg-card-begin: markdown--><h3 id="instalarwslenwindows">Instalar WSL en Windows:</h3>
<p>Para instalar <em>WSL</em> pulsa las teclar <strong>Windows + R</strong> y se abrir&#xE1; la ventana de ejecutar. A continuaci&#xF3;n escribe <strong>OptionalFeatures.exe</strong> y se abrir&#xE1; la ventana para instalar caracter&#xED;ticas opcionales.</p>
<p>Selecciona las opciones <strong>Plataforma de m&#xE1;quina virtual</strong> (Virtual Machine Platform) y <strong>Subsistema de Windows para Linux</strong> (Windows Subsystem for Linux).</p>
<p>Despu&#xE9;s de la instalaci&#xF3;n habr&#xE1; que <strong>reiniciar el sistema</strong>.</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="http://www.atopecode.net/content/images/2021/08/caracteristicas_opcionales_virtualizacion_y_wsl.png" class="kg-image" alt="Instalar Linux en Windows por medio de WSL2 (Windows Subsystem for Linux)." loading="lazy" width="555" height="492"></figure><p></p><!--kg-card-begin: markdown--><p>A continuaci&#xF3;n hay que <strong>actualizar el kernel de Linux</strong> en Windows.<br>
Se puede descargar desde este <a href="https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi" style="color: #0789c9; text-decoration: none;" target="_blank">enlace</a></p>
<p>Se instala como cualquier programa Windows, darle a siguiente y finalizar.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><p>Despu&#xE9;s de haber instalado WSL, reiniciado y actualizado el Kernel de Linux es necesario <strong>seleccionar la versi&#xF3;n de WSL que se ejecutar&#xE1; por defecto</strong>.<br>
Esto es necesario porque cuando se instala WSL se instalan conjuntamente la versi&#xF3;n 1 y la 2.</p>
<p>Para asegurarse que siempre se utilice la version WSL2 abre una terminal de <em>PowerShell como administrador</em> y teclea:</p>
<p><em><strong>wsl --set-default-version 2</strong></em></p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h3 id="instalardistribucindelinux">Instalar distribuci&#xF3;n de Linux:</h3>
<p>Para instalar una imagen de Linux hay que ir a la WindowsStore y buscar por <strong>&apos;Linux&apos;</strong>.<br>
La m&#xE1;s utilizada es Ubuntu, pero puedes instalar otras distros desde la WindowsStore. Ten en cuenta que algunas son de pago, aunque no es lo normal.<br>
Te dejo los enlaces para acceder directamente a las distros de Ubuntu:</p>
<p><a href="https://www.microsoft.com/store/apps/9N9TNGVNDL3Q" target="_blank" style="color: #0789c9; text-decoration: none;">Ubuntu 18.04 LST</a></p>
<p><a href="https://www.microsoft.com/store/apps/9n6svws3rx71" target="_blank" style="color: #0789c9; text-decoration: none;">Ubuntu 20.04 LST</a></p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h3 id="utilizarladistribucindelinuxconwsl2">Utilizar la distribuci&#xF3;n de Linux con WSL2:</h3>
<p>Una vez instalado WSL2 y la distribuci&#xF3;n de Linux solo falta arrancarla por medio del comando <strong>wsl</strong>.</p>
<p>Desde una terminal de Windows o PowerShell teclea lo siguiente:</p>
<ul>
<li><strong>wsl -l</strong><br>
Con este comando se listan todas las im&#xE1;genes que tienes instaladas en su sistema para ejecutar con wsl.<br>
En mi caso aparecen varias im&#xE1;genes porque tengo Docker instalado, pero la que nos intersa es la de <em>Ubuntu-20.04</em>.</li>
</ul>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="http://www.atopecode.net/content/images/2021/08/comando-wsl--l.png" class="kg-image" alt="Instalar Linux en Windows por medio de WSL2 (Windows Subsystem for Linux)." loading="lazy" width="577" height="204"></figure><p></p><!--kg-card-begin: markdown--><ul>
<li><strong>wsl -d &lt;nombre de la distribuci&#xF3;n que se quiere arrancar&gt;</strong><br>
Con este comando se arranca la distribuci&#xF3;n deseada escribiendo su nombre completo.<br>
Lo que suceder&#xE1; es que en la misma terminal de windows se nos abrir&#xE1; una session en la distribuci&#xF3;n Linux arrancada.<br>
El usuario de Linux es el mismo que usamos en Windows y tiene privilegios de <em>root</em> utilizando el comando <em>sudo</em>.</li>
</ul>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="http://www.atopecode.net/content/images/2021/08/comando-wsl--d-1.png" class="kg-image" alt="Instalar Linux en Windows por medio de WSL2 (Windows Subsystem for Linux)." loading="lazy" width="577" height="204"></figure><!--kg-card-begin: markdown--><p>Al igual que en Linux, podemos tener <strong>varias sesiones abiertas</strong>.<br>
Si desde otra terminal de Windows volvemos a ejecutar el comando <em>wsl -d</em>, como la distro Linux ya est&#xE1; arrancada lo que sucede es que se abre otra sessi&#xF3;n en una nueva consola.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><ul>
<li><strong>wsl -h</strong><br>
Con este comando se muestran todos los dem&#xE1;s par&#xE1;metros que podemos utilizar con wsl.</li>
</ul>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h3 id="compartirarchivosentrewindowsydistrolinuxwsl2">Compartir archivos entre Windows y distro Linux WSL2:</h3>
<p>Si te fijas en la imagen de arriba (despu&#xE9;s de ejecutar el comando <em>&apos;wsl -d&apos;</em>), nada m&#xE1;s arrancar la distribuci&#xF3;n de Ubuntu me indica que estoy en el path:<br>
<strong>very@OPTIMUS:/mnt/c</strong></p>
<p>Lo que quiere decir que en nuestro Linux se ha montado la partici&#xF3;n <strong>C:</strong> de nuestro disco duro donde tenemos instalado Windows.</p>
<p>Si escribimos en nuestro terminal Linux el comando <strong>&apos;ls -laF&apos;</strong> podremos comprobar que el contenido es el mismo que si escribimos desde la consola de Windows <em>c:\&gt;dir</em>.</p>
<p>Por lo tanto, abriendo desde Windows el explorador de archivos y copiando cualquier archivo en la partici&#xF3;n <strong>C:</strong> de nuestro disco duro podremos acceder desde Linux a su contenido y viceversa.</p>
<p>Como en cualquier Linux, el directorio <strong>/mnt</strong> es donde se montan dispositivos y particiones. Podemos navegar hasta la ra&#xED;z del sistema con el comando <strong>&apos;cd /&apos;</strong> y hasta nuestro home con <strong>&apos;cd ~&apos;</strong>.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h3 id="comunicacinporredentrewindowsydistrolinuxwsl2">Comunicaci&#xF3;n por red entre Windows y distro Linux WSL2:</h3>
<p>Por defecto la distro Linux instalada no tendr&#xE1; el firewall activado.<br>
He hecho pruebas activ&#xE1;ndolo y desp&#xFA;es de buscar m&#xE1;s info en internet he llegado a la conclusi&#xF3;n de que activar el firewall en nuestro nuevo Linux no es recomendable.<br>
Al activar el firewall de Linux se pierde la conexi&#xF3;n a internet desde nuestra distro.</p>
<p>Windows comparte la conexi&#xF3;n de red con nuestra distro Linux.<br>
Windows ya tiene un firewall configurado y al contenido de Linux solo accederemos desde nuestro Windows por lo tanto no es necesario activar ning&#xFA;n firewall y la conexi&#xF3;n de red es la misma para ambos sistemas operativos.</p>
<p>Si en nuestro nuevo Linux instalamos un servidor Http como Apache <em>(puerto 80)</em> o un Servicio Web SpringBoot <em>(puerto 8080)</em> por ejemplo, desde Windows podremos acceder a ellos de la misma forma que si se estuviesen ejecutando en el propio Windows:</p>
<p><code>http://localhost:80/miWeb</code><br>
<code>http://127.0.0.1:8080/api</code></p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: html--><!--a href="https://amzn.to/3IoDnz0" target="_blank"-->
<!--img src="http://www.atopecode.net/content/images/2022/01/Regalos-programador-informatico.png" class="kg-image fluidbox__thumb" alt="Regalos programador informático" srcset="http://www.atopecode.net/content/images/size/w600/2022/01/Regalos-programador-informatico.png 600w, http://www.atopecode.net/content/images/size/w1000/2022/01/Regalos-programador-informatico.png 1000w, http://www.atopecode.net/content/images/2022/01/Regalos-programador-informatico.png 1212w" sizes="(min-width: 720px) 720px" style="opacity: 0.9;"-->
<!--/a--><!--kg-card-end: html--><p></p><!--kg-card-begin: markdown--><h3 id="conclusin">Conclusi&#xF3;n:</h3>
<p>En este art&#xED;culo hemos visto como instalar una distribuci&#xF3;n Linux en Windows y ejecutarla por medio de WSL2 sin necesidad de tener que reiniciar el equipo para ejecutar Linux con arranque dual.</p>
<p>Es una manera mucho m&#xE1;s c&#xF3;moda de trabajar para todos los que programemos en Windows, ya que al fin y al cabo si te dedicas a programar Backend siempre vas a tener que trastear con Linux.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h3 id="enlacesdeinters">Enlaces de inter&#xE9;s:</h3>
<ul>
<li><a href="https://docs.microsoft.com/es-es/windows/wsl/about" target="_blank"> &#xBF;Qu&#xE9; es el Subsistema de Windows para Linux?</a></li>
<li><a href="https://www.howtogeek.com/744175/3-fun-linux-tools-to-run-on-windows-10-with-wsl/" target="_blank">3 Fun Linux Tools to Run on Windows 10 With WSL</a></li>
<li><a href="https://www.digitalocean.com/community/tutorials/como-instalar-java-con-apt-en-ubuntu-18-04-es" target="_blank">C&#xF3;mo instalar Java con apt en Ubuntu 18.04</a></li>
<li><a href="https://docs.microsoft.com/es-es/windows/wsl/interop" target="_blank"> Interoperabilidad de Windows con Linux</a></li>
<li><a href="https://www.campusmvp.es/recursos/post/VIDEO-Montar-Linux-Bash-en-Windows-10-y-primeros-pasos.aspx" target="_blank">Campus MVP - C&#xF3;mo instalar Linux en Windows y usar los dos sistemas a la vez: montando el subsistema de Linux (WSL2) </a></li>
</ul>
<!--kg-card-end: markdown--><p></p>]]></content:encoded></item><item><title><![CDATA[Optionals en Java - Parte 3. map() vs flatMap().]]></title><description><![CDATA[Diferencia entre utilizar 'Optional.map()' y 'Optional.flatMap()' con ejemplos prácticos.]]></description><link>http://www.atopecode.net/optionals-en-java-parte-3-map-vs-flatmap/</link><guid isPermaLink="false">60bf3b7b7a6ee263743f400a</guid><category><![CDATA[Java]]></category><dc:creator><![CDATA[Silverio Martínez García]]></dc:creator><pubDate>Wed, 09 Jun 2021 12:49:04 GMT</pubDate><media:content url="http://www.atopecode.net/content/images/2023/01/optionals_en_java_parte-3-map-vs-flatMap_1000x600.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="http://www.atopecode.net/content/images/2023/01/optionals_en_java_parte-3-map-vs-flatMap_1000x600.png" alt="Optionals en Java - Parte 3. map() vs flatMap()."><p>Esta es la 3&#xAA; parte de art&#xED;culos sobre <em>&apos;Optionals&apos;</em> en Java.<br>
En las entradas anteriores <em><a href="http://atopecode.net/optionals-en-java-parte-1-entendiendo-su-uso/" target="_blank">Optionals en Java - Parte 1. Entendiendo su uso</a></em> y <em><a href="http://atopecode.net/optionals-en-java-parte-2-evitando-validaciones-null-de-if-anidados/" target="_blank">Optionals en Java - Parte 2. Evitando validaciones &apos;null&apos; de &apos;if&apos; anidados</a></em> se explica como se deben utilizar los Optionals (y como no) y una de sus mayores ventajas para evitarnos las comprobaciones de &apos;null&apos; con &apos;if&apos; anidados de toda la vida.</p>
<p>Este art&#xED;culo es continuaci&#xF3;n del <a href="http://atopecode.net/optionals-en-java-parte-2-evitando-validaciones-null-de-if-anidados/" target="_blank">anterior</a> y trata de explicar cuando hay que utilizar <em>&apos;map()&apos;</em> y cuando <em>&apos;flatMap()&apos;</em> con nuestros <em>&apos;Optionals&apos;</em>.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><!--div style="width:100%"-->
<!--iframe style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="https://rcm-eu.amazon-adsystem.com/e/cm?ref=qf_sp_asin_til&t=atopecode-21&m=amazon&o=30&p=8&l=as1&IS2=1&asins=2409027407&linkId=55637d533f7d64229c26e9fc050d3cd5&bc1=000000&amp;lt1=_blank&fc1=333333&lc1=0066c0&bg1=ffffff&f=ifr"-->
    <!--/iframe-->
<!--/div--><!--kg-card-end: html--><p></p><!--kg-card-begin: markdown--><h3 id="optionalmap">Optional.map():</h3>
<p>Con el m&#xE9;todo <em>&apos;map()&apos;</em> debemos de env&#xED;ar como par&#xE1;metro una lambda de tipo <em>&apos;Function&apos;</em> que a su vez devuelve un tipo de dato en concreto. Posteriormente y de forma autom&#xE1;tica, <strong>el m&#xE9;todo <em>&apos;map()&apos;</em> envuelve nuestro tipo de dato devuelto dentro de un <em>&apos;Optional&apos;</em></strong> para poder seguir operando con &#xE9;l.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: html--><div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/cd76c407ce3829416ab146e91ac2359a.js"></script>
</div><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>En el ejemplo anterior, para <em>&apos;map()&apos;</em> estamos utilizando una lambda que devuelve el campo <em>&apos;Person.email&apos;</em> que es de tipo <em>&apos;String&apos;</em>, por lo tanto, estamos devolviendo directamente un <em>&apos;String&apos;</em>.<br>
Pero despu&#xE9;s de ejecutar nuestra funci&#xF3;n lambda, <strong><em>&apos;map()&apos;</em> autom&#xE1;ticamente crea un <em>&apos;Optional&lt;String&gt;&apos;</em> envolviendo nuestro <em>&apos;email&apos;</em></strong>. &#xC9;ste <em>&apos;Optional&apos;</em> devuelto es el valor que se sigue utilizando el flujo.</p>
<p>Por lo tanto, la l&#xED;nea <em>&apos;.orElse(null);&apos;</em> lo que est&#xE1; recibiendo es un <em>&apos;Optional&lt;String&gt;&apos;</em>.</p>
<!--kg-card-end: markdown--><p></p><p></p><!--kg-card-begin: markdown--><h3 id="optionalflatmap">Optional.flatMap():</h3>
<p>El m&#xE9;todo <em>&apos;flatMap()&apos;</em> tambi&#xE9;n recibe como par&#xE1;metro una funci&#xF3;n lambda de tipo <em>&apos;Function&apos;</em>, pero en este caso, desde nuestra lambda debemos devolver un <em>&apos;Optional&apos;</em> directamente.<br>
Es decir, a diferencia de como funciona <em>&apos;map()&apos;</em>, <strong>con <em>&apos;flatMap()&apos;</em> el resultado devuelto no se envuelve autom&#xE1;ticamente en un <em>&apos;Optional&apos;</em>.</strong><br>
Debemos devolver nosotros un objeto de tipo <em>&apos;Optional&apos;</em>.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: html--><div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/377cb4cd80cb5e9c8e6c3902d614cde1.js"></script>
</div><!--kg-card-end: html--><p></p><p></p><p></p><!--kg-card-begin: markdown--><h3 id="casoprcticoutilizandomapyflatmap">Caso pr&#xE1;ctico utilizando &apos;map()&apos; y &apos;flatMap()&apos;:</h3>
<p>Para ver cuando hay que utilizar un m&#xE9;todo u otro en la pr&#xE1;ctica, vamos a utilizar los modelos de datos del <a href="http://atopecode.net/optionals-en-java-parte-2-evitando-validaciones-null-de-if-anidados/" target="_blank">art&#xED;culo anterior</a> pero a&#xF1;adiendo unos cuantos m&#xE9;todos getter.<br>
Seguiremos utilizando el mismo repositorio.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: html-->Person.java
<div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/257c9e8e40abc32b81e88f83bdb44895.js"></script>
</div><!--kg-card-end: html--><p></p><!--kg-card-begin: html-->Address.java
<div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/a7fbaa2e773ce589b7a522981a4623ba.js"></script>
 <div><!--kg-card-end: html--><p></p><!--kg-card-begin: html-->State.java
<div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/3d2f0d858ded1ec9bfb85942a3549b0b.js"></script>
</div><!--kg-card-end: html--><p></p><!--kg-card-begin: html-->PersonRepository.java
<div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/716ad11ad963949bb73092da4300d59c.js"></script>
</div><!--kg-card-end: html--><!--kg-card-begin: html--><br><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>F&#xED;jate que en las clases <em>&apos;Person&apos;</em>, <em>&apos;Address&apos;</em> y <em>&apos;State&apos;</em> hemos a&#xF1;adido unos m&#xE9;todos getter que devuelven un objeto de tipo <em>&apos;Optional&apos;</em> para cada campo.<br>
Seguimos conservando los getters normales que devuelven el campo directamente.</p>
<p>Vamos ahora con unos cuantos tests para ver cuando hay que usar <em>&apos;map()&apos;</em> y <em>&apos;flatMap()&apos;</em>:</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h6 id="map">map():</h6>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/a322122d21142a756cbb4b2660c5ad6e.js"></script>
</div><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>En este test utilizamos <em>&apos;map()&apos;</em> para para obtener el campo &apos;Person.address.number&apos;.<br>
F&#xED;jate que en las expresiones lambda de cada <em>&apos;map()&apos;</em> se est&#xE1; llamando a los m&#xE9;todos getter que devuelven directamente el valor del campo correspondiente.<br>
No se est&#xE1; llamando a los getter que devuelven el <em>&apos;Optional&apos;</em> de cada campo.<br>
Ese es el motivo de utilizar &apos;<em>map()</em>&apos; en vez de <em>&apos;flatMap()&apos;</em>.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><h6>flatMap():</h6><!--kg-card-end: html--><!--kg-card-begin: html--><div style="width:100%">
<script src="https://gist.github.com/SilverioMG/b209c5cc375493ccab6cd67cdcda7476.js"></script>
<!--kg-card-begin: markdown--><p>En el test anterior se utiliza <em>&apos;flatMap()&apos;</em> porque en cada expresion lambda estamos llamando al m&#xE9;todo getter que devuelve un <em>&apos;Optional&apos;</em> con el valor de cada campo. As&#xED;, en este caso estamos devolviendo en cada <em>&apos;flatMap()&apos;</em> un <em>&apos;Optional<string>&apos;</string></em> para seguir utiliz&#xE1;ndolo en el flujo.</p>
<p>Si en este caso, en vez de <em>&apos;flatMap()&apos;</em> utiliz&#xE1;semos <em>&apos;map()&apos;</em>, lo que se estar&#xED;a devolviendo ser&#xED;a un <em>&apos;Optional&lt;Optional&lt;String&gt;&gt;&apos;</em> porque <em>&apos;map()&apos;</em> envuelve autom&#xE1;ticamente el resultado devuelto en un <em>&apos;Optional&apos;</em>, y no nos valdr&#xED;a.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><h6>map() y flatMap():</h6><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>En este &#xFA;ltimo test mostramos una combinaci&#xF3;n del uso de <em>&apos;map()&apos;</em> y <em>&apos;flatMap()&apos;</em>.</p>
<blockquote>
<p>Si en nuestra expresi&#xF3;n lambda utilizamos un getter que devuelve directamente el campo, entonces utilizamos <em>&apos;map()&apos;</em> (porque ya se encarga de envolver el resultado autom&#xE1;ticamente en un <em>&apos;Optional&apos;</em>).</p>
</blockquote>
<blockquote>
<p>Si en nuestra expresi&#xF3;n lambda utilizamos un getter que devuelve un <em>&apos;Optional&apos;</em> con el valor del campo, entonces utilizamos <em>&apos;flatMap()&apos;</em> (porque ya estamos devolviendo un <em>&apos;Optional&apos;</em>).</p>
</blockquote>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><script src="https://gist.github.com/SilverioMG/e0f053915028b07b9a9a416d3cecdf42.js"></script><!--kg-card-end: html--><p></p><!--kg-card-begin: html--><br><!--kg-card-end: html--><!--kg-card-begin: markdown--><h3 id="conclusin">Conclusi&#xF3;n:</h3>
<p>En este art&#xED;culo hemos visto en que casos hay que utilizar los m&#xE9;todo <em>&apos;map()&apos;</em> y <em>&apos;flatMap()&apos;</em> con nuestros <em>&apos;Optional&apos;</em> y la diferencia entre ellos.</p>
<p>Espero que te haya servido de utilidad.<br>
Nos vemos.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: html--><!--a href="https://amzn.to/3rDHuAj" target="_blank"-->
<!--img src="http://www.atopecode.net/content/images/2022/01/Teclados-programador.png" class="kg-image fluidbox__thumb" alt="" srcset="http://www.atopecode.net/content/images/size/w600/2022/01/Teclados-programador.png 600w, http://www.atopecode.net/content/images/size/w1000/2022/01/Teclados-programador.png 1000w, http://www.atopecode.net/content/images/2022/01/Teclados-programador.png 1332w" sizes="(min-width: 720px) 720px" style="opacity: 0.9;"-->
<!--/a--><!--kg-card-end: html--><!--kg-card-begin: html--><br><!--kg-card-end: html--><!--kg-card-begin: markdown--><h4 id="enlacesdeinters">Enlaces de inter&#xE9;s:</h4>
<ul>
<li><a href="https://github.com/SilverioMG/PruebaOptionalsJava" target="_blank">C&#xF3;digo del art&#xED;culo en GitHub</a></li>
<li><a href="https://stackify.com/optional-java/" target="_blank">Understanding, Accepting and Leveraging Optional in Java</a></li>
</ul>
<!--kg-card-end: markdown--></div></div></div>]]></content:encoded></item><item><title><![CDATA[Optionals en Java - Parte 2. Evitando validaciones 'null' de 'if' anidados.]]></title><description><![CDATA[Utilizando 'Optional' en Java para evitar validaciones 'null' de 'if' anidados.
Simulando 'Null Conditional Operator' de otros lenguajes como C#.]]></description><link>http://www.atopecode.net/optionals-en-java-parte-2-evitando-validaciones-null-de-if-anidados/</link><guid isPermaLink="false">60bddf357a6ee263743f3daf</guid><category><![CDATA[Java]]></category><dc:creator><![CDATA[Silverio Martínez García]]></dc:creator><pubDate>Mon, 07 Jun 2021 14:13:14 GMT</pubDate><media:content url="http://www.atopecode.net/content/images/2023/01/OPTION-1.PNG" medium="image"/><content:encoded><![CDATA[<img src="http://www.atopecode.net/content/images/2023/01/OPTION-1.PNG" alt="Optionals en Java - Parte 2. Evitando validaciones &apos;null&apos; de &apos;if&apos; anidados."><p></p><!--kg-card-begin: markdown--><p>En el <a href="http://atopecode.net/optionals-en-java-parte-1-entendiendo-su-uso/" target="_blank">art&#xED;culo anterior sobre Optionals en Java</a> hemos visto como y cuando se deben utilizar.</p>
<p>En este art&#xED;culo veremos una de las mayores ventajas de los <em>&apos;Optionals&apos;</em> a la hora de acceder a campos de objetos que pueden valer &apos;null&apos; y evitarnos el tener que comprobar si cada referencia vale &apos;null&apos; con &apos;if&apos; anidados.</p>
<p>En otros lenguajes de programaci&#xF3;n como C# o TypeScript esto se puede hacer de forma sencilla ya que se hace uso del <a href="https://www.informit.com/articles/article.aspx?p=2421572" target="_blank">Null Conditional Operator</a>.<br>
Pero en Java, para obtener la misma funcionalidad, de momento tenemos que hacer uso de los <em>&apos;Optionals&apos;</em>.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><!--iframe style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="https://rcm-eu.amazon-adsystem.com/e/cm?ref=qf_sp_asin_til&t=atopecode-21&m=amazon&o=30&p=8&l=as1&IS2=1&asins=2409027407&linkId=2c1abb77aa4ba54d605800274c70c1cd&bc1=000000&amp;lt1=_blank&fc1=333333&lc1=0066c0&bg1=ffffff&f=ifr"-->
<!--/iframe--><!--kg-card-end: html--><p></p><!--kg-card-begin: markdown--><h3 id="entrandoencontexto">Entrando en contexto:</h3>
<p>Normalmente en Java cuando queremos acceder al campo de un objeto hay que comprobar primero que dicho objeto no valga &apos;null&apos;. La cosa se complica cuando queremos acceder a un campo que est&#xE1; dentro del campo de otro objeto y as&#xED; sucesivamente:</p>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/17c2886de3d37e3e19abb1e3cddf7c71.js"></script>
</div><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>Al final nuestro c&#xF3;digo se convierte en un mont&#xF3;n de &apos;ifs&apos; anidados cuando en C# por ejemplo, el mismo c&#xF3;digo ser&#xED;a de la siguiente manera:</p>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><div style="width:100%;">
    <script src="https://gist.github.com/SilverioMG/8c4a9ad43af7f6478830da8973463cd0.js"></script>
</div><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>Si alguno de los campos vale &apos;null&apos; se asigna &apos;null&apos; directamente a &apos;stateName&apos; y no se siguen evaluando los dem&#xE1;s.</p>
<p>Este resultado es el mismo que con el ejemplo de Java pero con mucho menos c&#xF3;digo y mucho m&#xE1;s legible.</p>
<p>Vamos a ver ahora como se consigue esto en Java y de paso encontr&#xE1;ndole un poco m&#xE1;s de utilidad a los <em>&apos;Optionals&apos;</em>.<br>
Partiremos del mismo proyecto de tests que usamos en el <a href="http://atopecode.net/optionals-en-java-parte-1-entendiendo-su-uso/" target="_blank">art&#xED;culo anterior</a> con nuestros modelos de datos y el repositorio.<br>
Puedes <a href="https://github.com/SilverioMG/PruebaOptionalsJava" target="_blank"> descargar el c&#xF3;digo fuente desde GitHub.</a></p>
<!--kg-card-end: markdown--><p></p><p></p><!--kg-card-begin: markdown--><h3 id="empezamosapicarcdigo">Empezamos a picar c&#xF3;digo:</h3>
<!--kg-card-end: markdown--><!--kg-card-begin: html-->Person.java
<div style="width:100%">
<script src="https://gist.github.com/SilverioMG/ab782fe03a544b91a74380a945adc03b.js"></script>
</div><!--kg-card-end: html--><p></p><!--kg-card-begin: html-->Address.java
<div style="width:100%">
<script src="https://gist.github.com/SilverioMG/e054e743ae940f797a603b3da063cd31.js"></script>
</div><!--kg-card-end: html--><p></p><!--kg-card-begin: html-->State.java
<div style="width:100%">
<script src="https://gist.github.com/SilverioMG/43595bb3440e882887d8108856d6b51b.js"></script>
</div><!--kg-card-end: html--><p></p><!--kg-card-begin: html-->PersonRepository.java
<div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/716ad11ad963949bb73092da4300d59c.js"></script>
</div><!--kg-card-end: html--><!--kg-card-begin: html--><br>
<br><!--kg-card-end: html--><!--kg-card-begin: markdown--><h3 id="evitandonullvalidationsanidadas">Evitando &apos;Null Validations&apos; anidadas:</h3>
<p>Vamos a ver una serie de tests para recuperar el valor de un campo sin tener que utilizar &apos;if&apos; anidados para comprobar valores &apos;null&apos;:</p>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/1766586c53ed3ebd023aa6d559d68d80.js"></script>
</div><!--kg-card-end: html--><p></p><!--kg-card-begin: markdown--><p>En este test primero se crea un objeto <em>&apos;Person&apos;</em> cuyo campo <em>&apos;Person.address&apos;</em> vale &apos;null&apos;.<br>
Para comprobar que se ha creado correctamente el objeto, se intenta acceder a dicho campo y como vale &apos;null&apos; se lanza una &apos;NullPointerException&apos;.</p>
<p>A continuaci&#xF3;n se crea un <em>&apos;Optional&apos;</em> a partir de nuestro objeto &apos;Person&apos;.<br>
Se utiliza <em>&apos;map()&apos;</em> para intentar acceder al campo <em>&apos;Person.address.street&apos;</em> de forma segura sin tener que comprobar valores &apos;null&apos;.<br>
La funci&#xF3;n <em>&apos;map()&apos;</em> acepta como par&#xE1;metro una lambda de tipo <em>&apos;Function&apos;</em> que devolver&#xE1; un campo del objeto actual que a su vez se <strong>envuelve dentro de un <em>&apos;Optional&apos;</em></strong> para poder seguir evalu&#xE1;ndolo.<br>
Si el campo devuelto vale &apos;null&apos;, la funci&#xF3;n <em>&apos;map()&apos;</em> devolver&#xE1; un <em>&apos;Optional.empty&apos;</em>.</p>
<p>La l&#xED;nea de c&#xF3;digo <em>&apos;.map(person -&gt; person.getAddress())&apos;</em> lo que har&#xED;a ser&#xED;a devolver un <em>&apos;Optional&lt;Address&gt;&apos;</em> con el valor del campo <em>&apos;person.getAddress()&apos;</em>. Pero como en nuestro caso <em>&apos;person.getAddress()&apos;</em> vale &apos;null&apos;, la funci&#xF3;n <em>&apos;map()&apos;</em> devuelve un <em>&apos;Optional.empty&apos;</em>.</p>
<p>La siguiente l&#xED;nea <em>&apos;.map(address -&gt; address.getStreet())&apos;</em> no se llega a ejecutar ya que se est&#xE1; ejecutando sobre un <em>&apos;Optional.empty&apos;</em> y salta a la siguiente funci&#xF3;n <strong>sin lanzar NullPointerException</strong>.</p>
<p>En la &#xFA;ltima l&#xED;nea <em>&apos;.orElseGet(() -&gt; null);&apos;</em> lo que hacemos es indicar el valor final que se devolver&#xE1; en caso de recibir un <em>&apos;Optional.empty&apos;</em>. En nuestro caso hemos indicado que se devuelva &apos;null&apos; pero se prodr&#xED;a haber indicado otro valor o incluso llamar a un m&#xE9;todo que nos devuelva el valor necesario.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: html--><div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/ff943b60458fa545f328b58d9782712f.js"></script>
</div><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>En este otro test se vuelve a intentar recuperar el campo <em>&apos;Person.getAddress.getStreet()&apos;</em> pero ahora con &#xE9;xito, ya que partimos de un objeto &apos;Person&apos; con valor para su campo &apos;address&apos;.</p>
<p>Primero se crea el objeto &apos;Person&apos; y se comprueba que &apos;address&apos; no vale &apos;null&apos; recuperando el campo &apos;street&apos;.</p>
<p>A continuaci&#xF3;n se crea un <em>&apos;Optional&apos;</em> a partir de nuestro objeto &apos;Person&apos;.<br>
Volvemos a utilizar <em>&apos;map()&apos;</em> para obtener el valor de cada campo de forma segura evitando &apos;NullPointerExceptions&apos;.</p>
<p>En la l&#xED;nea <em>&apos;.map(Person::getAddress)&apos;</em> se devuelve un &apos;Optional&lt;Address&gt;&apos; con el valor del campo <em>&apos;Person.getAddress()&apos;</em>.<br>
F&#xED;jate como en este caso en vez de env&#xED;ar como par&#xE1;metro al m&#xE9;todo <em>&apos;map()&apos;</em> una lambda, se est&#xE1; enviando directamente un <em>&apos;method reference&apos;</em>, pero el efecto es justamente el mismo que en el test anterior.</p>
<p>La l&#xED;nea <em>&apos;.map(Address::getStreet)&apos;</em> en este caso s&#xED; se ejecuta porque recibe un <em>&apos;Optional&lt;Address&gt;&apos;</em> que no est&#xE1; vac&#xED;o.<br>
En la lambda se recibe el objeto de tipo <em>&apos;Address&apos;</em> y se devuelve un &apos;Optional&lt;String&gt;&apos; con el valor del campo &apos;Address.street&apos; correspondiente.</p>
<p>La l&#xED;nea <em>&apos;.orElseGet(() -&gt; null);&apos;</em> en este caso no llega a ejecutarse, porque no recibe un <em>&apos;Optional.empty&apos;</em>.<br>
Se devuelve directamente el valor del campo &apos;Person.getAddress.getStreet()&apos;.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><p>Vamos a ver otro par de tests a&#xF1;adiendo un campo m&#xE1;s, ahora intentaremos obtener el valor de &apos;Person.getAdress().getState().getName()&apos; cuando &apos;state&apos; vale &apos;null&apos; y cuando tiene valor.<br>
La explicaci&#xF3;n de los tests es la misma, simplemente se a&#xF1;ade un m&#xE9;todo adicional <em>&apos;map()&apos;</em>  al <em>&apos;Optional&apos;</em>:</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/feb0e69aacb389f722e2588baf8e5318.js"></script>
</div><!--kg-card-end: markdown--><p></p><!--kg-card-begin: html--><div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/016a21d81ada11e82d5ae3ad2f00f433.js"></script>
</div><!--kg-card-end: html--><p></p><p></p><!--kg-card-begin: markdown--><h3 id="conclusin">Conclusi&#xF3;n:</h3>
<p>En este art&#xED;culo se comprueba como se puede obtener el valor del campo &apos;Person.getAdress().getStreet()&apos; y &apos;Person.getAdress().getState().getName()&apos; sin utilizar comprobaci&#xF3;n de &apos;null&apos; ni &apos;if&apos; anidados.</p>
<p>Si lo comparamos con el <em>Null Conditional Operator</em> de C#, en Java con <em>&apos;Optional&apos;</em> tenemos un mecanismo para que en caso de que alg&#xFA;n campo intermedio valga &apos;null&apos; poder devolver el valor que nosotros queramos.<br>
En C# se devolver&#xED;a directamente &apos;null&apos; y para poder devolver otro valor habr&#xED;a que utilizar un operador a mayores.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: html--><!--a href="https://amzn.to/3IoDnz0" target="_blank"-->
<!--img src="http://www.atopecode.net/content/images/2022/01/Regalos-programador-informatico.png" class="kg-image fluidbox__thumb" alt="Regalos programador informático" srcset="http://www.atopecode.net/content/images/size/w600/2022/01/Regalos-programador-informatico.png 600w, http://www.atopecode.net/content/images/size/w1000/2022/01/Regalos-programador-informatico.png 1000w, http://www.atopecode.net/content/images/2022/01/Regalos-programador-informatico.png 1212w" sizes="(min-width: 720px) 720px" style="opacity: 0.9;"-->
<!--/a--><!--kg-card-end: html--><p></p><!--kg-card-begin: markdown--><h4 id="enlacesdeinters">Enlaces de inter&#xE9;s:</h4>
<ul>
<li><a href="https://github.com/SilverioMG/PruebaOptionalsJava" target="_blank">C&#xF3;digo del art&#xED;culo en GitHub</a></li>
<li><a href="https://www.informit.com/articles/article.aspx?p=2421572" target="_blank">Using the New Null Conditional Operator in C# 6</a></li>
<li><a href="https://www.oracle.com/technical-resources/articles/java/java8-optional.html" target="_blank">Tired of Null Pointer Exceptions? Consider Using Java SE 8&apos;s &quot;Optional&quot;!</a></li>
</ul>
<!--kg-card-end: markdown--><p></p>]]></content:encoded></item><item><title><![CDATA[Optionals en Java - Parte 1. Entendiendo su uso.]]></title><description><![CDATA[Entendiendo el uso de 'Optional' en Java. Proyecto de test con ejemplos prácticos.]]></description><link>http://www.atopecode.net/optionals-en-java-parte-1-entendiendo-su-uso/</link><guid isPermaLink="false">60b771a27a6ee263743f39e2</guid><category><![CDATA[Java]]></category><dc:creator><![CDATA[Silverio Martínez García]]></dc:creator><pubDate>Fri, 04 Jun 2021 10:43:10 GMT</pubDate><media:content url="http://www.atopecode.net/content/images/2023/01/optionals_en_java_parte-1-entendiendo_su_uso_alternative_1000x600.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="http://www.atopecode.net/content/images/2023/01/optionals_en_java_parte-1-entendiendo_su_uso_alternative_1000x600.png" alt="Optionals en Java - Parte 1. Entendiendo su uso."><p>A partir de la versi&#xF3;n de Java 8 podemos utilizar <strong>Optional&lt;T&gt;</strong> para evitar las indeseables <em>NullPointerExceptions</em>.</p>
<p>Los objetos de tipo <em>Optional</em> almacenan un campo que puede tener valor o no (&apos;null&apos;). Ofrecen una serie de m&#xE9;todos para comprobar si hay valor almacenado, poder recuperarlo o devolver un valor por defecto en caso de que el valor almacenado sea &apos;null&apos;.</p>
<p>De primeras parece una gran utilidad, pero no todo es oro lo que reluce :)<br>
&#xBF;Cuando hay que utilizar este tipo de objeto? &#xBF;Ya no volver&#xE9; a utilizar &apos;null&apos; nunca m&#xE1;s? &#xBF;De verdad es &#xFA;til utilizar <em>Optional</em>?<br>
En este art&#xED;culo intentar&#xE9; explicar el uso para el que est&#xE1; pensado <em>Optional</em> y las buenas pr&#xE1;cticas para utilizarlo.</p>
<p>Veremos que s&#xED; que compensa utilizar <em>Optional</em> en nuestro c&#xF3;digo pero siguiendo una serie de normas y teniendo en cuenta cual es su verdadero prop&#xF3;sito.</p>
<p>Vamos al l&#xED;o.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><!--iframe style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="https://rcm-eu.amazon-adsystem.com/e/cm?ref=qf_sp_asin_til&t=atopecode-21&m=amazon&o=30&p=8&l=as1&IS2=1&asins=2409027407&linkId=2c1abb77aa4ba54d605800274c70c1cd&bc1=000000&amp;lt1=_blank&fc1=333333&lc1=0066c0&bg1=ffffff&f=ifr"-->
<!--/iframe--><!--kg-card-end: html--><p></p><!--kg-card-begin: markdown--><h3 id="cuandoutilizaroptional">Cuando utilizar Optional:</h3>
<p><em>Optional</em> est&#xE1; pensado &#xFA;nicamente para <strong>utilizarlo como valor de retorno de nuestros m&#xE9;todos.</strong><br>
Sirve para indicar al programador que un m&#xE9;todo en cuesti&#xF3;n puede devolver valor &apos;null&apos; y al mismo tiempo se le ofrece un mecanismo para que pueda recuperar de forma segura dicho valor y utilizarlo sin que se produzca &apos;NullPointerException&apos;*:</p>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/b4b2b5f3a541fc7bf87521a4b9e205b7.js"></script>
</div><!--kg-card-end: html--><p></p><!--kg-card-begin: markdown--><p>Entonces, teniendo en cuenta lo anterior, <strong>NO se deber&#xE1; utilizar <em>Optional</em> en los siguientes casos</strong>:</p>
<ol>
<li>Como tipo de dato para un campo de nuestras clases:</li>
</ol>
<div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/e2d82ef9b156aed4ba92db9c6bb6eb79.js"></script>
<div><!--kg-card-end: markdown--><hr><!--kg-card-begin: markdown--><ol start="2">
<li>En par&#xE1;metros de un constructor:</li>
</ol>
<script src="https://gist.github.com/SilverioMG/49b8a44fcf247057067c8429f9513c36.js"></script><!--kg-card-end: markdown--><hr><!--kg-card-begin: markdown--><ol start="3">
<li>En par&#xE1;metros de una m&#xE9;todo:</li>
</ol>
<script src="https://gist.github.com/SilverioMG/7466c1183af47baa6eafbae0e17e5407.js"></script><!--kg-card-end: markdown--><p></p><hr><!--kg-card-begin: markdown--><p>Ahora tenemos claro que <strong>solo debemos utilizar <em>Optional</em> como valor de retorno de nuestros m&#xE9;todos que puedan devolver &apos;null&apos; en alg&#xFA;n momento</strong>.</p>
<p>A continuaci&#xF3;n voy a mostrar un proyecto de Test para poder ver distintos casos en los que se utiliza <em>Optional</em>, como se le puede sacar m&#xE1;s partido y unas cuantas normas a seguir para evitar que el propio uso de <em>Optional</em> nos provoque alg&#xFA;n que otro problemilla.</p>
<p>Puedes descargar el <em><strong><a href="https://github.com/SilverioMG/PruebaOptionalsJava" target="_blank">c&#xF3;digo fuente desde GitHub</a></strong></em>.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><br>
<br><!--kg-card-end: html--><!--kg-card-begin: markdown--><h3 id="preparndonosparapicarcdigo">Prepar&#xE1;ndonos para picar c&#xF3;digo:</h3>
<p>Vamos a crear nuestras clases que servir&#xE1;n como modelo de datos para los tests.<br>
En este caso tendremos una clase <em>&apos;Person&apos;</em> que tendr&#xE1; un campo del tipo <em>&apos;Address&apos;</em> que a su vez tambi&#xE9;n tendr&#xE1; un campo del tipo <em>&apos;State&apos;</em>.</p>
<p>Adem&#xE1;s tendremos nuestro peque&#xF1;o repositorio llamado <em>&apos;PersonRepository&apos;</em> para proveernos de distintas construcciones de <em>&apos;Person&apos;</em> que utilizaremos para los tests.</p>
<p>Por &#xFA;ltimo tenemos nuestra clase de tests &apos;OptionalUnitTest&apos; donde iremos explicando uno por uno cada uno de los ejemplos y llegando a las conclusiones necesarias sobre el uso de <em>&apos;Optional&apos;</em>.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: html-->Person.java
<div style="width:100%">
<script src="https://gist.github.com/SilverioMG/ab782fe03a544b91a74380a945adc03b.js"></script>
</div><!--kg-card-end: html--><p></p><!--kg-card-begin: html-->Address.java
<div style="width:100%">
<script src="https://gist.github.com/SilverioMG/e054e743ae940f797a603b3da063cd31.js"></script>
</div><!--kg-card-end: html--><p></p><!--kg-card-begin: html-->State.java
<div style="width:100%">
<script src="https://gist.github.com/SilverioMG/43595bb3440e882887d8108856d6b51b.js"></script>
</div><!--kg-card-end: html--><p></p><!--kg-card-begin: html-->PersonRepository.java
<div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/716ad11ad963949bb73092da4300d59c.js"></script>
</div><!--kg-card-end: html--><p></p><!--kg-card-begin: html-->OptionalUnitTest.java
<div style="width:100%">
<script src="https://gist.github.com/SilverioMG/cc5e18360761bfd36a9c19ac0dd864be.js"></script>
</div><!--kg-card-end: html--><!--kg-card-begin: html--><br>
<br><!--kg-card-end: html--><p></p><!--kg-card-begin: markdown--><h3 id="creandooptionals">Creando Optionals:</h3>
<p>Vamos a echarle un ojo a los tests correspondientes a la creaci&#xF3;n de <em>Optionals</em> para ver en detalle los distintos casos que podemos encontrarnos:</p>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/fa645b6e9a2f106e0ab6e3b7fc4c7ee8.js"></script>
</div><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>Podemos observar que si usamos <em>&apos;Optional.of(valor)&apos;</em> debemos de asegurarnos primero que <em>&apos;valor&apos;</em> no valga &apos;null&apos;, porque en ese caso se lanza una <em>&apos;NullPointerException&apos;</em> (justamente lo que se pretende evitar utilizando Optionals).</p>
<p>Pero si en su lugar utilizamos <em>&apos;Optional.ofNullable(valor)&apos;</em> no tendremos nunca problemas, ya que en caso de que <em>&apos;valor&apos;</em> valga &apos;null&apos; se crear&#xE1; un <em>&apos;Optional&apos;</em> vac&#xED;o y no se lanza ninguna Exception.</p>
<p>Por lo tanto aqu&#xED; tenemos una norma importante que debemos seguir a la hora de crear nuestros objetos de tipo <em>&apos;Optional&apos;</em>:</p>
<blockquote>
<p><strong>A la hora de crear un objeto de tipo <em>&apos;Optional&apos;</em> utilizar siempre <em>&apos;Optional.ofNullable(valor)&apos;</em> en lugar de <em>&apos;Optional.of(valor)&apos;</em></strong>.</p>
</blockquote>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><br>
<br><!--kg-card-end: html--><p></p><!--kg-card-begin: markdown--><h3 id="recuperandoelvalordeunoptional">Recuperando el valor de un Optional:</h3>
<p>Veamos ahora las distintas formas de recuperar el valor que contiene un <em>&apos;Optional&apos;</em>:</p>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/f8c1b69ab0968468af676c567af8d7a7.js"></script>
</div><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>Primero vemos que para comprobar si un <em>&apos;Optional&apos;</em> tiene valor o no podemos utilizar el m&#xE9;todo <em>&apos;isEmpty()&apos;</em>.</p>
<p>Cuando queremos recuperar el valor que contiene el <em>&apos;Optional&apos;</em>, si usamos el m&#xE9;todo <em>&apos;get()&apos;</em> y el <em>&apos;Optional&apos;</em> est&#xE1; vac&#xED;o (su contenido es &apos;null&apos;) se produce una <em>&apos;NoSuchElementException&apos;</em>.<br>
Si por el contrario nuestro <em>&apos;Optional&apos;</em> tiene valor entonces con <em>&apos;get()&apos;</em> lo recuperamos.<br>
En este caso habr&#xED;a que utilizar antes el m&#xE9;todo <em>&apos;isEmpty()&apos;</em> para saber si podemos utilizar <em>&apos;get()&apos;</em> y que no nos lance la Exception.</p>
<p>Por &#xFA;ltimo observamos como utilizando el m&#xE9;todo <em>&apos;orElse()&apos;</em> podemos recuperar el valor de nuestro <em>&apos;Optional&apos;</em> y en caso de que est&#xE9; vac&#xED;o podemos indicar como par&#xE1;metro el valor que queremos que se devuelva evitando que se lance la <em>&apos;NoSuchElementException&apos;</em> del m&#xE9;todo <em>&apos;get()&apos;</em>.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h6 id="cuidadoconelmtodoorelse">Cuidado con el m&#xE9;todo <em>&apos;orElse()&apos;</em>:</h6>
<p>Hemos visto que a la hora de recuperar el valor del <em>&apos;Optional&apos;</em> es mejor utilizar siempre el m&#xE9;todo <em>&apos;orElse()&apos;</em> en vez de <em>&apos;get()&apos;</em> para evitar que se produzca la dichosa <em>&apos;NoSuchElementException&apos;</em>.</p>
<p>Pero hay que tener cuidado porque el m&#xE9;todo <em>&apos;orElse()&apos;</em> se ejecutar&#xE1; siempre, incluso cuando el <em>&apos;Optional&apos;</em> tenga valor para devolver y en teor&#xED;a no har&#xED;a falta que se ejecutase el <em>&apos;orElse()&apos;</em> porque no vamos a utilizar su valor.<br>
Lo comprobamos en el siguiente test:</p>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><div style="width:100%">
<script src="https://gist.github.com/SilverioMG/55bf278854c73052b4f855ba3157c6b7.js"></script>
</div><!--kg-card-end: html--><p></p><!--kg-card-begin: markdown--><p>Esto puede no ser un problema cuando en el <em>&apos;orElse()&apos;</em> estamos indicando como par&#xE1;metro directamente un valor como por ejemplo <em>&apos;orElse(&quot;DefaultValue&quot;)&apos;</em> o <em>&apos;orElse(null)&apos;</em>.</p>
<p>Pero en los casos en los que en el <em>&apos;orElse()&apos;</em> se est&#xE9; ejecutando otro m&#xE9;todo para obtener el valor a devolver, s&#xED; que podemos tener problemas.<br>
En casos como <em>&apos;orElse(getUserFromDB())&apos;</em> o <em>&apos;orElse(getUserHttp())&apos;</em>, si el m&#xE9;todo utilizado como par&#xE1;metro hace una consulta a la B.D. o una petici&#xF3;n a un servicio web hay que tener en cuenta que siempre se va ejecutar, incluso en el caso de que el <em>&apos;Optional&apos;</em> tenga valor para devolver y el valor devuelto por <em>&apos;orElse()&apos;</em> no se vaya a utilizar nunca.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h6 id="recuperandoelvalordeunoptionaldeformasegurayeficiente">Recuperando el valor de un <em>&apos;Optional&apos;</em> de forma segura y eficiente:</h6>
<p>Llegados a este punto podemos pensar que esto de los <em>&apos;Optionals&apos;</em> es un poco mierda, parece que dan m&#xE1;s problemas que otra cosa, pero tranquilo, coge aire y recuerda que Java es para machotes :)</p>
<p>Llega al rescate el m&#xE9;todo <em>&apos;orElseGet()&apos;</em>:</p>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/1ab74b3da8b969b65e9d9b7a6a4774e7.js"></script>
</div><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>El m&#xE9;todo <em>&apos;orElseGet()&apos;</em> recibe como par&#xE1;metro una lambda del tipo Supplier para indicar el valor que queremos devolver en caso de que el <em>&apos;Optional&apos;</em> est&#xE9; vac&#xED;o (el valor que contiene es &apos;null&apos;), pero en este caso, la funci&#xF3;n lambda que se env&#xED;a en <em>&apos;orElseGet()&apos;</em> solo se ejecuta si de verdad hace falta devolver dicho valor.<br>
Es decir, con <em>&apos;orElseGet()&apos;</em>, si el <em>&apos;Optional&apos;</em> tiene valor para devolver, nunca se ejecuta la funci&#xF3;n lambda.</p>
<p>Entonces ahora ya s&#xED; que podemos devolver desde nuestra funci&#xF3;n lambda un valor directamente, una consulta a la B.D. o una petici&#xF3;n Http estando seguros de que solo se ejecutar&#xE1;n si es necesario.</p>
<p>Otra vez, podemos deducir otra norma a seguir, esta vez en el caso de que queramos recupar el valor de nuestro <em>&apos;Optional&apos;</em>:</p>
<blockquote>
<p><strong>A la hora de recuperar el valor de un objeto <em>&apos;Optional&apos;</em> utilizar siempre el m&#xE9;todo <em>&apos;orElseGet()&apos;</em> en lugar de <em>&apos;get()&apos;</em> o <em>&apos;orElse()&apos;</em></strong>.</p>
</blockquote>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><br>
<br><!--kg-card-end: html--><p></p><!--kg-card-begin: markdown--><h3 id="conclusin">Conclusi&#xF3;n:</h3>
<p>En este art&#xED;culo hemos visto cual es el prop&#xF3;sito del uso de <em>&apos;Optional&apos;</em> y como debe usarse.</p>
<p>Tambi&#xE9;n hemos deducido unas cuantas normas a seguir para que su uso sea una ventaja en vez de complicarnos m&#xE1;s las cosas.</p>
<p>A modo de resumen:</p>
<blockquote>
<p><strong>1: Utilizar <em>&apos;Optional&apos;</em> &#xFA;nicamente como valor de retorno de nuestros m&#xE9;todos que puedan devolver &apos;null&apos; en alg&#xFA;n momento.</strong></p>
</blockquote>
<blockquote>
<p><strong>2: No utilizar nunca <em>&apos;Optional&apos;</em> en los siguientes casos:</strong><br>
-Como tipo de dato para un campo de nuestras clases.<br>
-En par&#xE1;metros de un constructor.<br>
-En par&#xE1;metros de una m&#xE9;todo.</p>
</blockquote>
<blockquote>
<p><strong>3: A la hora de crear un objeto de tipo &apos;Optional&apos; utilizar siempre &apos;Optional.ofNullable(valor)&apos; en lugar de &apos;Optional.of(valor)&apos;.</strong></p>
</blockquote>
<blockquote>
<p><strong>4: A la hora de recuperar el valor de un objeto &apos;Optional&apos; utilizar siempre el m&#xE9;todo &apos;orElseGet()&apos; en lugar de &apos;get()&apos; o &apos;orElse()&apos;.</strong></p>
</blockquote>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><p>En el pr&#xF3;ximo art&#xED;culo sobre <em>&apos;Optionals&apos;</em> veremos como sacarle partido a su uso y las ventajas pr&#xE1;cticas que ofrece.</p>
<p>Espero que te haya servido de ayuda y que a partir de ahora empieces a utilizar <em>&apos;Optional&apos;</em> en tu c&#xF3;digo.<br>
Nos vemos.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: html--><!--a href="https://amzn.to/3rDHuAj" target="_blank"-->
<!--img src="http://www.atopecode.net/content/images/2022/01/Teclados-programador.png" class="kg-image fluidbox__thumb" alt="" srcset="http://www.atopecode.net/content/images/size/w600/2022/01/Teclados-programador.png 600w, http://www.atopecode.net/content/images/size/w1000/2022/01/Teclados-programador.png 1000w, http://www.atopecode.net/content/images/2022/01/Teclados-programador.png 1332w" sizes="(min-width: 720px) 720px" style="opacity: 0.9;"-->
<!--/a--><!--kg-card-end: html--><p></p><!--kg-card-begin: markdown--><h4 id="enlacesdeinters">Enlaces de inter&#xE9;s:</h4>
<ul>
<li><a href="https://github.com/SilverioMG/PruebaOptionalsJava" target="_blank">C&#xF3;digo del art&#xED;culo en GitHub</a></li>
<li><a href="https://stackify.com/optional-java/" target="_blank">Understanding, Accepting and Leveraging Optional in Java</a></li>
<li><a href="https://blogs.oracle.com/javamagazine/12-recipes-for-using-the-optional-class-as-its-meant-to-be-used" target="_blank">12 recipes for using the Optional class as it&#x2019;s meant to be used</a></li>
</ul>
<!--kg-card-end: markdown--><p></p><p></p></div></div>]]></content:encoded></item><item><title><![CDATA[Uso básico de Streams en Java]]></title><description><![CDATA[Ejemplos de los usos más frecuentes con Streams en Java]]></description><link>http://www.atopecode.net/uso-de-streams-en-java8/</link><guid isPermaLink="false">60a7c69d7a6ee263743f3637</guid><category><![CDATA[Java]]></category><dc:creator><![CDATA[Silverio Martínez García]]></dc:creator><pubDate>Mon, 24 May 2021 11:23:40 GMT</pubDate><media:content url="http://www.atopecode.net/content/images/2021/07/Java-Streams_1000x600.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="http://www.atopecode.net/content/images/2021/07/Java-Streams_1000x600.png" alt="Uso b&#xE1;sico de Streams en Java"><p>Desde la versi&#xF3;n 8 de Java podemos utilizar cualquier clase que implemente la interfaz <strong>Collection</strong> como si fuese un <strong>Stream</strong> con las ventajas que nos ofrece la programaci&#xF3;n funcional y las <strong>Expresiones Lambda</strong>.</p>
<p>Una vez que te acostumbres a utilizar los streams ya no querr&#xE1;s volver a hacer bucles del tipo <em>&apos;for(Class var1: listType){}&apos;</em></p>
<p>A continuaci&#xF3;n voy a poner unos ejemplos de los usos m&#xE1;s frecuentes con streams.<br>
Puedes descargar el <em><strong><a href="https://github.com/SilverioMG/PruebaStreamsJava" target="_blank">c&#xF3;digo fuente desde GitHub</a></strong></em>.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><!--iframe style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="https://rcm-eu.amazon-adsystem.com/e/cm?ref=qf_sp_asin_til&t=atopecode-21&m=amazon&o=30&p=8&l=as1&IS2=1&asins=2409027407&linkId=2c1abb77aa4ba54d605800274c70c1cd&bc1=000000&amp;lt1=_blank&fc1=333333&lc1=0066c0&bg1=ffffff&f=ifr"-->
    <!--/iframe--><!--kg-card-end: html--><p></p><!--kg-card-begin: markdown--><h3 id="preparndonosparapicarcdigo">Prepar&#xE1;ndonos para picar c&#xF3;digo:</h3>
<p>Vamos a crear una clase <em>&apos;Book&apos;</em> que ser&#xE1; nuestro model y un repositorio <em>&apos;BookRepository&apos;</em> que devolver&#xE1; una lista de Books para poder utilizar en nuestros tests. As&#xED; mostramos como funciona cada funci&#xF3;n por separado y con su correspondiente comprobaci&#xF3;n del resultado final.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: html-->Book.java
<div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/5da25d68a18cc4cbe55af5353d929bc1.js"></script>
</div><!--kg-card-end: html--><p></p><!--kg-card-begin: html-->BookRepository.java
<div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/ad60f0df176a0be3bcf51a819d52e0ec.js"></script>
</div><!--kg-card-end: html--><p></p><!--kg-card-begin: html-->StreamsUnitTest.java
<div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/ccef88da4ea1fb748c986388a9cb794a.js"></script>
</div><!--kg-card-end: html--><p></p><!--kg-card-begin: markdown--><h3 id="funcionesdelstream">Funciones del Stream:</h3>
<p>Ahora veamos uno por uno cada test para explicar un poco como funciona cada funci&#xF3;n del stream utilizada.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: html--><div style="width:100%">
    <h6>Collect():<h6>
    <script src="https://gist.github.com/SilverioMG/6850eaa63da0ca79557f93625e4b89ff.js"></script>
</h6></h6></div><!--kg-card-end: html--><!--kg-card-begin: markdown--><p><em>&apos;Collect()&apos;</em> se utiliza para indicar el tipo de collection en la que se devolver&#xE1; el resultado final de todas las operaciones realizadas sobre el <em>stream</em>.<br>
En este ejemplo no se realiza ninguna operaci&#xF3;n sobre el stream y simplemente se convierte el contenido a un <em>List</em> y a un <em>Set</em>.<br>
En el caso del <em>Set</em> se comprueba como no se guardan los valores repetidos del <em>&apos;stream&apos;</em> debido a la propia naturaleza del <em>Set</em> que impide guardar valores duplicados.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: html--><div style="width:100%">
    <h6>Peek():<h6>
<script src="https://gist.github.com/SilverioMG/e86b30369eec8860a4f710abad83e296.js"></script>
</h6></h6></div><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>El m&#xE9;todo <em>&apos;peek()&apos;</em> recibe como par&#xE1;metro una lambda de tipo <em>&apos;Consumer&apos;</em> para poder utilizar y/o modificar cada elemento del <em>stream</em>.<br>
Normalmente se utiliza para mostrar por consola el contenido del stream en cada momento.</p>
<p>Tambi&#xE9;n se puede modificar el valor de alg&#xFA;n campo del objeto &apos;Book&apos; como en nuestro ejemplo pero no es muy aconsejable, ya que en la programaci&#xF3;n funcional  se aconseja utilizar objetos inmutables.<br>
No es una buena pr&#xE1;ctica cambiar el valor del objeto directamente.<br>
En este caso en particular ser&#xED;a mejor utilizar el m&#xE9;todo <em>&apos;map()&apos;</em> que veremos m&#xE1;s adelante devolviendo una copia modificada de cada elemento de la lista.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: html--><div style="width:100%">
    <h6>Map():<h6>
<script src="https://gist.github.com/SilverioMG/9d34770f70f0fa42f06a75ae87bb52ce.js"></script>
</h6></h6></div><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>El m&#xE9;todo <em>&apos;map()&apos;</em> recibe como par&#xE1;metro una lambda de tipo &apos;Function&apos; por lo que debemos indicar una funci&#xF3;n que recibe como par&#xE1;metro de entrada cada elemento del <em>stream</em> y devuelve un objeto que puede ser un tipo de dato distinto o del mismo.</p>
<p>Se utiliza para modificar el contenido del stream a partir de un punto determinado del flujo.</p>
<p>En nuestro ejemplo el <em>&apos;stream&apos;</em> comienza con un flujo de objetos de tipo <em>&apos;Book&apos;</em> y a partir del <em>&apos;map()&apos;</em> se convierte en un flujo de tipo <em>&apos;String&apos;</em> al devolver el t&#xED;tulo de cada libro.</p>
<p>Se utiliza <em>&apos;peek()&apos;</em> para mostrar por consola los valores devueltos por <em>&apos;map()&apos;</em> y finalmente se recolecta el resultado como una lista.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: html--><div style="width:100%">
    <h6>ForEach():<h6>
<script src="https://gist.github.com/SilverioMG/6482bb98d3fc7f529afb8acb56a4e9b5.js"></script>
</h6></h6></div><!--kg-card-end: html--><!--kg-card-begin: html--><div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/e6dbc2a18fb18718b31263ccd3548f64.js"></script>
</div><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>Con <em>&apos;forEach()&apos;</em> recorremos cada elemento del stream para realizar alguna acci&#xF3;n con &#xE9;l. Recibe como par&#xE1;metro una lambda de tipo <em>&apos;Consumer&apos;</em>.</p>
<p>Si nos fijamos bien <em>&apos;forEach()&apos;</em> es un m&#xE9;todo que no devuelve ning&#xFA;n valor.<br>
A diferencia del resto de funciones vistas, <em>&apos;forEach()&apos;</em> es una <strong>operaci&#xF3;n de terminaci&#xF3;n</strong>, es decir, no se pueden seguir realizando operaciones encadenadas con el mismo stream.</p>
<p>En nuestro primer ejemplo recorremos el stream de libros para modificar su precio y mostramos por consola su nuevo valor.</p>
<p>En el segundo ejemplo primero obtenemos los t&#xED;tulos de los libros por medio de la funci&#xF3;n <em>&apos;map()&apos;</em> y utilizamos <em>&apos;forEach()&apos;</em> para comprobar el resultado del test.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: html--><div style="width:100%">
    <h6>Filter():<h6>
<script src="https://gist.github.com/SilverioMG/63b312c020a2e96ab2626dceb3070b4e.js"></script>
</h6></h6></div><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>Con <em>&apos;filter()&apos;</em> como su nombre indica, lo que hacemos es filtrar de todos los elementos del stream solo aquellos que cumplan una determinada condici&#xF3;n.<br>
Recibe como par&#xE1;metro una lambda de tipo <em>&apos;Predicate&apos;</em> la cual debe devolver &apos;true&apos; solo en aquellos elementos que seguir&#xE1;n en el stream y &apos;false&apos; para aquellos que se deben eliminar.</p>
<p>En nuestro ejemplo usamos <em>&apos;filter&apos;</em> para seleccionar de todos los libros solo aquellos cuyo precio cumpla la condici&#xF3;n indicada.<br>
Tambi&#xE9;n usamos <em>&apos;peek()&apos;</em> a continuaci&#xF3;n para mostrar por consola los libros seleccionados.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: html--><div style="width:100%">
    <h6>FindFirst():<h6>
<script src="https://gist.github.com/SilverioMG/55c706bfa64e80983f5747a4785d261b.js"></script>
</h6></h6></div><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>La funci&#xF3;n <em>&apos;findFirst()&apos;</em> se utiliza para devolver el primer elemento encontrado del <em>&apos;stream&apos;</em>. Se suele utilizar en combinaci&#xF3;n con otras funciones cuando hay que seleccionar un &#xFA;nico valor del Stream que cumpla determinadas condiciones.</p>
<p><em>&apos;findFirst()&apos;</em> devuelve un objeto de tipo <em>&apos;Optional&apos;</em> para poder indicar un valor por defecto en caso de que no se pueda devolver ning&#xFA;n elemento del <em>&apos;stream&apos;</em>.</p>
<p>En el ejemplo se filtran los libros por su precio, a continuaci&#xF3;n se muestran por consola los libros filtrados y finalmente se selecciona el primero que cumpla la condici&#xF3;n.<br>
En caso de que ning&#xFA;n libro cumpliese la condici&#xF3;n del <em>&apos;filter()&apos;</em> indicamos que se devuelva como valor <em>&apos;null&apos;</em>.</p>
<p>Este es un buen ejemplo para observar una caracter&#xED;stica propia del funcionamiento de los <em>&apos;streams&apos;</em>, que es la <strong>&apos;LazyEvaluation&apos;</strong>.<br>
Las funciones del <em>&apos;stream&apos;</em> se van ejecutando una tras otra por cada elemento (libro) y luego se pasa al siguiente elemento sobre el cual se vuelven a ejecutar todas las fuciones y as&#xED; sucesivamente por cada elemento del stream.</p>
<p>En nuestro ejemplo hay 2 libros que cumplen la condici&#xF3;n del <em>&apos;filter()&apos;</em>, pero si lo ejecutamos observamos que en el <em>&apos;peek()&apos;</em> solo se muestra por consola el primero de ellos.<br>
Esto es debido a que en cuanto el primer libro que cumple la condici&#xF3;n del <em>&apos;filter()&apos;</em> llega al <em>&apos;findFirst()&apos;</em> el stream se finaliza devolviendo dicho elemento, con lo cual sobre el siguiente libro ya no se llega a ejecutar el correspondiente <em>&apos;filter()&apos;</em> ni el resto de funciones del <em>&apos;stream&apos;</em>.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: html--><div style="width:100%">
    <h6>ToArray():<h6>
<script src="https://gist.github.com/SilverioMG/a7b756dd85d2dc0e27b80c2aac9cc7c3.js"></script>
</h6></h6></div><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>Con <em>&apos;toArray()&apos;</em> podemos convertir cualquier tipo de <em>&apos;Collection&apos;</em> en un <em>&apos;array&apos;</em> de forma sencilla.</p>
<p>Las 2 primeras l&#xED;neas del ejemplo sirven de recordatorio para ver como se puede convertir una lista en un <em>&apos;array&apos;</em> sin utilizar <em>&apos;streams&apos;</em>.</p>
<p>A continuaci&#xF3;n se muestra como convertir un <em>&apos;stream&apos;</em> (obtenido a partir de cualquier <em>&apos;Collection&apos;</em>) en un <em>&apos;array&apos;</em>.</p>
<p>Y por &#xFA;ltimo se muestra como obtener un <em>&apos;stream&apos;</em> partir de un <em>&apos;array&apos;</em> y se utiliza para mostrar por consola sus valores.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: html--><div style="width:100%">
    <h6>Sorted():<h6>
<script src="https://gist.github.com/SilverioMG/d2c1ccdf6e94ea2922401ba2e7a69cda.js"></script>
</h6></h6></div><!--kg-card-end: html--><!--kg-card-begin: markdown--><p><em>&apos;sorted()&apos;</em> se utiliza para ordenar los elementos del <em>&apos;stream&apos;</em>.<br>
Recibe como par&#xE1;metro una lambda de tipo <em>&apos;Comparator&apos;</em> para que podamos indicar la l&#xF3;gica de ordenaci&#xF3;n.</p>
<p>En nuestro ejemplo estamos ordenando los libros por su campo &apos;author&apos; en orden ascendente sin tener en cuenta las may&#xFA;sculas y min&#xFA;sculas.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: html--><div style="width:100%">
    <h6>Min():<h6>
<script src="https://gist.github.com/SilverioMG/b9231eb0b58597c3bd4e21699c7d7787.js"></script>
</h6></h6></div><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>Con <em>&apos;min()&apos;</em> se obtiene el elemento del <em>&apos;stream&apos;</em> con el valor m&#xED;nimo calculado a partir de la lambda de tipo <em>&apos;Comparator&apos;</em> que indiquemos como par&#xE1;metro.</p>
<p><em>&apos;min()&apos;</em> devuelve un objeto de tipo <em>&apos;Optional&apos;</em> para poder indicar un valor por defecto en caso de que no se pueda devolver ning&#xFA;n elemento del <em>&apos;stream&apos;</em>.</p>
<p>En el ejemplo se obtiene el libro con el menor precio.<br>
Se indica en el objeto <em>&apos;Optional&apos;</em> devuelto por <em>&apos;min()&apos;</em> que en caso de no encontrar ning&#xFA;n elemento se devuelva <em>&apos;null&apos;</em>.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: html--><div style="width:100%">
    <h6>Max():<h6>
<script src="https://gist.github.com/SilverioMG/e91ebdd2f0f508c89d55a2c0b8a66902.js"></script>
</h6></h6></div><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>Con &apos;max()&apos; se obtiene el elemento del &apos;stream&apos; con el valor m&#xE1;ximo calculado a partir de la lambda de tipo &apos;Comparator&apos; que indiquemos como par&#xE1;metro.</p>
<p><em>&apos;max()&apos;</em> devuelve un objeto de tipo <em>&apos;Optional&apos;</em> para poder indicar un valor por defecto en caso de que no se pueda devolver ning&#xFA;n elemento del <em>&apos;stream&apos;</em>.</p>
<p>En el ejemplo se obtiene el libro con el mayor precio.<br>
Se indica en el objeto <em>&apos;Optional&apos;</em> devuelto por <em>&apos;max()&apos;</em> que en caso de no encontrar ning&#xFA;n elemento se devuelva <em>&apos;null&apos;</em>.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: html--><div style="width:100%">
    <h6>Distinct():<h6>
<script src="https://gist.github.com/SilverioMG/6facdc6ee762267646e093aee5af9e38.js"></script>
</h6></h6></div><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>Con <em>&apos;distinct()&apos;</em> se seleccionan los elementos distintos dentro del <em>&apos;stream&apos;</em> eliminando los duplicados.</p>
<p>En nuestro ejemplo primero a&#xF1;adimos un nuevo libro a la lista con un nombre repetido. A continuaci&#xF3;n convertimos la lista en un <em>&apos;stream&apos;</em> y utilizamos <em>&apos;map()&apos;</em> para obtener los t&#xED;tulos de los libros y a continuaci&#xF3;n con <em>&apos;distinct()&apos;</em> se excluyen los valores duplicados.</p>
<p>Hay que tener en cuenta que si usase <em>&apos;distinct&apos;</em> directamente sobre el objeto <em>&apos;Book&apos;</em> del stream, habr&#xED;a que implementar su correspondiente m&#xE9;todo <em>&apos;equals()&apos;</em> que es el que se utiliza para comparar la igualdad de los elementos entre s&#xED;.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: html--><div style="width:100%">
    <h6>allMatch(), anyMath() y noneMatch():<h6>
<script src="https://gist.github.com/SilverioMG/3b0935654ba37891f3f1f4e01f31fe0f.js"></script>
</h6></h6></div><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>Las funciones <em>&apos;allMatch()&apos;</em>, <em>&apos;anyMatch()&apos;</em> y <em>&apos;noneMatch()&apos;</em> devuelven un <em>&apos;boolean&apos;</em> despu&#xE9;s de ejecutar la lambda de tipo <em>&apos;Predicate&apos;</em> que reciben como par&#xE1;metro sobre cada elemento del <em>&apos;stream&apos;</em>.</p>
<ul>
<li><em>&apos;allMatch()&apos;</em>: Devuelve <em>true</em> en caso de que la condici&#xF3;n de la lambda se cumpla para <em>todos</em> los elementos del <em>&apos;stream&apos;</em> y <em>false</em> en caso de que <em>alg&#xFA;n</em> elemento no cumpla la condici&#xF3;n.<br>
Se utiliza evaluaci&#xF3;n por cortocircuito, es decir, en cuanto la condici&#xF3;n no se cumple para un elemento ya no se sigue comparando con el resto y <em>&apos;allMatch()&apos;</em> devolver&#xE1; <em>false</em>.</li>
</ul>
<p>En nuestro ejemplo comprobamos que todos los elementos del <em>&apos;stream&apos;</em> sean par (el resto de la divisi&#xF3;n entre 2 debe dar O como resultado). Como existe un elemento que no es par, el resultado de <em>&apos;allMatch()&apos;</em> es <em>false</em>.</p>
<ul>
<li><em>&apos;anyMatch()&apos;</em>: Devuelve <em>true</em> en caso de que la condici&#xF3;n de la lambda se cumpla para <em>alg&#xFA;n</em> elemento del <em>&apos;stream&apos;</em> y <em>false</em> si <em>ning&#xFA;n</em> elemento cumple la condici&#xF3;n.<br>
Tambi&#xE9;n se utiliza evaluaci&#xF3;n por cortocircuito, es decir, en cuanto la condici&#xF3;n se cumpla para un elemento ya no se sigue comparando con el resto y <em>&apos;anyMatch&apos;</em> devolver&#xE1; <em>true</em>.</li>
</ul>
<p>En nuestro ejemplo comprobamos que alg&#xFA;n elemento del <em>&apos;stream&apos;</em> sea par. Como el primer elemento seleccionado ya es par, &apos;<em>anyMatch()</em>&apos; devolver&#xE1; <em>true</em> y no seguir&#xE1; evaluando el resto de elementos.</p>
<ul>
<li><em>&apos;noneMatch()&apos;</em>: Devuelve <em>true</em> en caso de que <em>ning&#xFA;n</em> elemento del <em>&apos;stream&apos;</em> cumpla la condici&#xF3;n de la lambda y <em>false</em> en caso de que <em>alg&#xFA;n</em> elemento cumpla la condici&#xF3;n.<br>
Tambi&#xE9;n se utiliza evaluaci&#xF3;n por cortocircuito, por lo que en cuanto se encuentre un elemento que cumpla la condici&#xF3;n ya no se sigue comparando con el resto y <em>&apos;noneMatch()&apos;</em> devolver&#xE1; <em>false</em>.</li>
</ul>
<p>En nuestro ejemplo comprobamos que ning&#xFA;n elemento del <em>&apos;stream&apos;</em> sea m&#xFA;ltiplo de 3, como s&#xED; que existe un elemento que cumple dicha condici&#xF3;n, <em>&apos;noneMatch()&apos;</em> devolver&#xE1; <em>false</em> y no seguir&#xE1; evuluando el resto de elementos.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><p><em><strong>Conclusi&#xF3;n:</strong></em><br>
En este art&#xED;culo hemos visto las funciones m&#xE1;s comunes para utilizar con los streams de Java8 y versiones posteriores.</p>
<p>Existen m&#xE1;s funciones y caracter&#xED;sticas que no se cubren en este art&#xED;culo pero a continuaci&#xF3;n dejo un par de enlaces para seguir investigando sobre su uso.</p>
<p>Espero que este art&#xED;culo te haya servido de utilidad y a partir de ahora empieces a utilizar los streams y las lambda para ser un ATopeCoder de verdad :)</p>
<p>Nos vemos.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: html--><!--a href="https://amzn.to/3IoDnz0" target="_blank"-->
<!--img src="http://www.atopecode.net/content/images/2022/01/Regalos-programador-informatico.png" class="kg-image fluidbox__thumb" alt="Regalos programador informático" srcset="http://www.atopecode.net/content/images/size/w600/2022/01/Regalos-programador-informatico.png 600w, http://www.atopecode.net/content/images/size/w1000/2022/01/Regalos-programador-informatico.png 1000w, http://www.atopecode.net/content/images/2022/01/Regalos-programador-informatico.png 1212w" sizes="(min-width: 720px) 720px" style="opacity: 0.9;"-->
<!--/a--><!--kg-card-end: html--><p></p><!--kg-card-begin: markdown--><h4 id="enlacesdeinters">Enlaces de inter&#xE9;s:</h4>
<ul>
<li><a href="https://github.com/SilverioMG/PruebaStreamsJava" target="_blank">C&#xF3;digo del art&#xED;culo en GitHub</a></li>
<li><a href="https://stackify.com/streams-guide-java-8/" target="_blank">A Guide to Java Streams in Java 8: In-Depth Tutorial With Examples</a></li>
<li><a href="https://www.baeldung.com/java-8-streams-introduction" target="_blank">Baeldung - Introduction to Java 8 Streams</a></li>
</ul>
<!--kg-card-end: markdown--><p></p>]]></content:encoded></item><item><title><![CDATA[Logging en SpringBoot -  Configurando LogBack.]]></title><description><![CDATA[Configuración básica de Logging en un proyecto SpringBoot.]]></description><link>http://www.atopecode.net/logging-en-springboot-configurando-logback/</link><guid isPermaLink="false">604b9b727a6ee263743f33ab</guid><category><![CDATA[SpringBoot]]></category><category><![CDATA[Backend]]></category><category><![CDATA[Java]]></category><dc:creator><![CDATA[Silverio Martínez García]]></dc:creator><pubDate>Sat, 13 Mar 2021 18:25:20 GMT</pubDate><media:content url="http://www.atopecode.net/content/images/2021/03/SpringBoot-Logging_1000x600.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="http://www.atopecode.net/content/images/2021/03/SpringBoot-Logging_1000x600.png" alt="Logging en SpringBoot -  Configurando LogBack."><p>El logging es una parte fundamental que necesitamos en nuestros Servicios Web.<br>
Siempre vamos a tener que echar mano de ellos para consultar alg&#xFA;n error inesperado o comprobar el comportamiento de nuestro servicio una vez desplegado.</p>
<p>SpringBoot por defecto utiliza una implementaci&#xF3;n de <a href="http://www.slf4j.org/" target="_blank">slf4j</a> llamada <a href="http://logback.qos.ch/" target="_blank">LogBack</a><br>
Si no configuramos nada, por defecto springboot solo muestra logs por consola y a partir del nivel &apos;INFO&apos; y posteriores (no se mostrar&#xE1;n los niveles &apos;TRACE&apos; ni &apos;DEBUG&apos;).</p>
<p>Siempre es bueno recordar los LogLevels ordenados de menor a mayor importacia:</p>
<ul>
<li><strong>trace (the least serious)</strong></li>
<li><strong>debug</strong></li>
<li><strong>info</strong></li>
<li><strong>warn</strong></li>
<li><strong>error</strong></li>
<li><strong>fatal (the most serious)</strong></li>
</ul>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h3 id="configurandonuestrologger">Configurando nuestro Logger:</h3>
<p>Para configurar como se muestran los mensajes de Logging, con que formato y por donde deben imprimirse (consola y/o archivo) debemos de a&#xF1;adir un archivo con el nombre <strong>logback-spring.xml</strong> en la carpeta <strong>/resources</strong> de nuestro proyecto.<br>
Un ejemplo de configuraci&#xF3;n puede ser esta:</p>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/b7d83644d832e2163fe560d33c00a638.js"></script>
</div><!--kg-card-end: html--><p></p><!--kg-card-begin: markdown--><h3 id="seccionesdelarchivologbackspringxml">Secciones del archivo logback-spring.xml:</h3>
<p><strong>property:</strong> Aqu&#xED; se definen valores que se pueden repetir a lo largo del archivo .xml y en lugar de copiar/pegar dicho valor en todas las partes necesarias se utiliza una propiedad para hacer referencia a dicho valor. As&#xED; si el valor cambia solo tendremos que cambiarlo en dicha propiedad sin tener que ir buscando por todo el archivo la cadena correspondiente a modificar.</p>
<p><strong>appender:</strong> Los appender sirven para declarar las &apos;salidas&apos; de nuestros logs.<br>
Se pueden configurar uno o m&#xE1;s appenders para mostrar logs por consola o para que se escriban en un archivo. Tambi&#xE9;n se define el formato del mensaje y otra opciones como por ejemplo el truncado de nuestros archivos de logs cada cierto tiempo o al llegar a un tama&#xF1;o en disco determinado.</p>
<p><strong>root:</strong> En esta secci&#xF3;n se configura el Logging por defecto de nuestra aplicaci&#xF3;n para cualquier paquete o clase.<br>
Aqu&#xED; es donde se utilizan los &apos;<em>appenders</em>&apos; declarados previamente, para indicar cuales ser&#xE1;n las salidas de nuestros logs por defecto.<br>
En nuestro archivo .xml hemos definido que por defecto cualquier log que se haga debe salir solo por la consola.<br>
Podr&#xED;amos haber definido que se guardasen tambi&#xE9;n en un archivo utilizando el appender <em>&apos;RollingFile&apos;</em> o incluso declarar otro <em>appender</em> para guardarlos en otro archivo distinto y utilizar ambos para guardar los logs en 2 archivos distintos.<br>
Se pueden utilizar tantos <em>appenders</em> como nos hagan falta seg&#xFA;n nuestras necesidades.<br>
En la propiedad &apos;level&apos; se indica el nivel de log a partir del cual se mostrar&#xE1;n los mensajes. En nuestro ejemplo solo se mostrar&#xE1;n los mensajes a partir del level &apos;info&apos; y posteriores (no se mostrar&#xE1;n los logs con level &apos;trace&apos; o &apos;debug&apos;).</p>
<p><strong>logger:</strong> Esta secci&#xF3;n sirve para configurar el Logging al igual que en la secci&#xF3;n <em>root</em> pero solo para los Loggers que pertenezcan a un paquete y clase espec&#xED;ficos.<br>
En el par&#xE1;metro &apos;name&apos; se indica el paquete y/o clase correspondiente.<br>
A continuaci&#xF3;n se indican los <em>appenders</em> que utilizar&#xE1;n los Loggers de dicho paquete y/o clase.<br>
En nuestro arhivo .xml hemos definido que los mensajes de log de todas las clases que pertenezcan al paquete <em>&apos;net.atopecode&apos;</em> (y descendientes) se escriban por consola y en archivo de disco.<br>
En la propiedad &apos;level&apos; se indica el nivel de log a partir del cual se mostrar&#xE1;n los mensajes. En nuestro ejemplo se muestran todos los niveles de logging ya que hemos indicado el level &apos;trace&apos; y posteriores.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h3 id="utilizandoelloggerdesdenuestrocdigo">Utilizando el Logger desde nuestro c&#xF3;digo:</h3>
<p>Para utilizar el Logger en nuestro c&#xF3;digo es necesario indicar a que clase pertenece. Dependiendo del paquete/clase a la que pertenece el Logger, a la hora de mostrar el mensaje se utilizar&#xE1; un appender u otro seg&#xFA;n lo que hayamos configurado en la secci&#xF3;n <em>&apos;logger&apos;</em> de nuestro archivo <em>&apos;logback-spring.xml&apos;</em></p>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/4c4214b5bb50d33a38382f5ed05543b5.js"></script>
</div><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>En este ejemplo se est&#xE1; creando el Logger para la clase <strong>&apos;PruebaLoggingComponent&apos;</strong> que coincide que es la misma clase donde se va a utilizar el Logger. Normalmente la clase para la que se define el Logger es la misma en la que se va a utilizar pero no tiene porque ser as&#xED;.<br>
Lo que importa a la hora de declarar nuestro Logger con respecto a la configuraci&#xF3;n del archivo .xml es el par&#xE1;metro &apos;.class&apos; que se para como par&#xE1;metro en el m&#xE9;todo <em>&apos;LoggerFactory.getLogger()&apos;</em>.</p>
<p>En este caso nuestro Logger utilizar&#xE1; la secci&#xF3;n</p>
<pre><code class="language-xml">    &lt;logger name=&quot;net.atopecode&quot; level=&quot;trace&quot; additivity=&quot;false&quot;&gt;
        &lt;appender-ref ref=&quot;RollingFile&quot; /&gt;
        &lt;appender-ref ref=&quot;Console&quot; /&gt;
    &lt;/logger&gt;
</code></pre>
<p>de nuestro archivo <em>&apos;logback-spring.xml&apos;</em> ya que la clase &apos;PruebaLoggingComponent&apos; est&#xE1; dentro del paquete &apos;.net.atopecode.*&apos;.<br>
Por lo tanto se escribir&#xE1;n los mensajes de log por consola y en archivo de disco al mismo tiempo.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h3 id="propiedadadditivity">Propiedad &apos;additivity&apos;:</h3>
<p>Hay que tener en cuenta que la secci&#xF3;n <em>&apos;root&apos;</em> de nuestro archivo <em>&apos;logback-spring.xml&apos;</em> tambi&#xE9;n afecta a nuestro Logger, ya que hace referencia a cualquier Logger definido dentro del proyecto.</p>
<p>Para estos casos se utiliza la propiedad <em>&apos;additivity&apos;</em> que por defecto vale &apos;true&apos;. En nuestro caso al asignar <em>additivity=&quot;false&quot;</em> estamos indicando que se utilice la secci&#xF3;n <em>&apos;logger&apos;</em> de nuestro archivo .xml que m&#xE1;s se ajuste al &apos;paquete/clase&apos; pero no todas las otras secciones que puedan hacer referencia a nuestro Logger.</p>
<p>Si no hubi&#xE9;semos indicado <em>additivity=&quot;false&quot;</em> en este caso, nuestros mensajes habr&#xED;an salido por duplicado en la consola (una vez por la secci&#xF3;n <em>root</em> y otra por la secci&#xF3;n <em>logger</em>) y tambi&#xE9;n se escribir&#xED;an en un archivo (por la secci&#xF3;n <em>logger</em>).</p>
<p>Para entender mejor esta parte pongamos como ejemplo otro archivo .xml igual al anterior, pero esta vez a&#xF1;adiremos otra secci&#xF3;n <em>&apos;logger&apos;</em> indicando el nombre de la clase (somos m&#xE1;s restrictivos que solo indicando el paquete) para que se ajuste m&#xE1;s a nuestro Logger declarado en el c&#xF3;digo Java.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><div style="width:100%">
    <script src="https://gist.github.com/SilverioMG/8db93514965e1d41d13223c1c953c02a.js"></script>
</div><!--kg-card-end: html--><!--kg-card-begin: markdown--><p>Ahora nuestro Logger declarado en la clase Java <em>&quot;PruebaLoggingComponent&quot;</em> utilizar&#xE1; la nueva secci&#xF3;n <em>&apos;logger&apos;</em> de nuestro archivo .xml ya que es la que m&#xE1;s se ajusta porque est&#xE1; indicando directamente el nombre de clase:</p>
<pre><code class="language-xml">    &lt;logger name=&quot;net.atopecode.authservice.component.PruebaLoggingComponent&quot; level=&quot;debug&quot; additivity=&quot;false&quot;&gt;
        &lt;appender-ref ref=&quot;Console&quot; /&gt;
    &lt;/logger&gt;
</code></pre>
<p>Nuestros mensajes saldr&#xE1;n ahora solo por consola y a partir del level debug.</p>
<p>Si <strong>no</strong> hubi&#xE9;semeos puesto la propiedad <em>additivity=&quot;false&quot;</em>, adem&#xE1;s de nuestra nueva secci&#xF3;n se estar&#xED;a tambi&#xE9;n utilizando la anterior a ella:</p>
<pre><code class="language-xml">    &lt;logger name=&quot;net.atopecode&quot; level=&quot;trace&quot; additivity=&quot;false&quot;&gt;
        &lt;appender-ref ref=&quot;RollingFile&quot; /&gt;
        &lt;appender-ref ref=&quot;Console&quot; /&gt;
    &lt;/logger&gt;
</code></pre>
<p>En cuyo caso se mostrar&#xED;an los mensajes en archivo y por duplicado en la consola.<br>
Es una buena pr&#xE1;ctica utilizar siempre <strong>additivity=&quot;false&quot;</strong> para que solo una secci&#xF3;n <em>&apos;logger&apos;</em> de nuestro archivo xml afecte a los mensajes de nuestros Loggers.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h3 id="conclusin">Conclusi&#xF3;n:</h3>
<p>En este art&#xED;culo hemos visto una forma b&#xE1;sica de configurar los Logs en SpringBoot.<br>
Existen muchas m&#xE1;s opciones pero con esta configuraci&#xF3;n se puede empezar a trabajar en un proyecto guardando los logs en un archivo adem&#xE1;s de mostrarlos por consola y especificando a partir de que nivel queremos que se muestren.</p>
<p>Espero que este art&#xED;culo haya sido de utilidad.<br>
A Tope Codear!!!</p>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><!--a href="https://amzn.to/3rDHuAj" target="_blank"-->
<!--img src="http://www.atopecode.net/content/images/2022/01/Teclados-programador.png" class="kg-image fluidbox__thumb" alt="" srcset="http://www.atopecode.net/content/images/size/w600/2022/01/Teclados-programador.png 600w, http://www.atopecode.net/content/images/size/w1000/2022/01/Teclados-programador.png 1000w, http://www.atopecode.net/content/images/2022/01/Teclados-programador.png 1332w" sizes="(min-width: 720px) 720px" style="opacity: 0.9;"-->
<!--/a--><!--kg-card-end: html--><p></p><!--kg-card-begin: markdown--><h4 id="enlacesdeinters">Enlaces de inter&#xE9;s:</h4>
<ul>
<li><a href="https://www.baeldung.com/spring-boot-logging" target="_blank">Logging in Spring Boot - Baeldung Blog</a></li>
<li><a href="https://www.baeldung.com/logback" target="_blank">A Guide To Logback  - Baeldung Blog</a></li>
<li><a href="https://stackoverflow.com/questions/5653062/how-can-i-configure-logback-to-log-different-levels-for-a-logger-to-different-de" target="_blank">How can I configure Logback to log different levels for a logger to different destinations?</a></li>
<li><a href="https://reflectoring.io/springboot-logging/" target="_blank">Logging In Spring Boot - Reflectoring Blog</a></li>
<li><a href="https://reflectoring.io/tracing-with-spring-cloud-sleuth/" target="_blank">Tracing in Distributed Systems with Spring Cloud Sleuth</a></li>
</ul>
<!--kg-card-end: markdown--><p></p>]]></content:encoded></item><item><title><![CDATA[Arrancando servicio web SpringBoot en IntelliJ Idea Community]]></title><description><![CDATA[Opciones para ejecutar un proyecto SpringBoot en el IDE IntelliJ Idea Community (versión gratuita).]]></description><link>http://www.atopecode.net/arrancando-servicio-web-springboot-en-intellij-idea-community/</link><guid isPermaLink="false">6043a2ee6c716851f88b5bed</guid><category><![CDATA[SpringBoot]]></category><category><![CDATA[Java]]></category><category><![CDATA[Backend]]></category><dc:creator><![CDATA[Silverio Martínez García]]></dc:creator><pubDate>Fri, 05 Feb 2021 16:23:00 GMT</pubDate><media:content url="http://www.atopecode.net/content/images/2021/03/SpringBoot-IntelliJIdea---1000x600.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="http://www.atopecode.net/content/images/2021/03/SpringBoot-IntelliJIdea---1000x600.png" alt="Arrancando servicio web SpringBoot en IntelliJ Idea Community"><p>Para proyectos SpringBoot se suele utilizar como IDE el <a href="https://spring.io/tools" target="_blank">Spring Tool Suite</a> que es un Eclipse tuneado y que incorpora por defecto los plugins para desarrollar con SpringBoot. Aunque el STS est&#xE1; muy bien hay que reconocer que el IntelliJ es mucho mejor IDE.</p>
<p>El problema es que con la versi&#xF3;n Community de IntelliJ (gratis) no se pueden instalar los plugins para SpringBoot y el desarrollo no es tan trivial.</p>
<p>Hay varias maneras para desarrollar un proyecto SpringBoot con el IntelliJIdea Community, en este art&#xED;culo muestro varias opciones para hacerlo.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h3 id="creandoelproyectospringboot">Creando el proyecto SpringBoot:</h3>
<p>Desde la p&#xE1;gina de <a href="https://start.spring.io/" target="_blank">Spring Initializr</a> creamos un proyecto SpringBoot seleccionado todas las librer&#xED;as que vamos a utilizar. Lo descargamos y posteriormente lo abrimos en el IntelliJ.</p>
<p>Dependiendo de como hemos decidido crear el proyecto en la web de Spring Initializr se nos abrir&#xE1; como un proyecto maven o gradle.</p>
<p>Ahora ya podemos empezar a picar c&#xF3;digo y desarrollar nuestro servicio web.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h3 id="arrancandoelserviciowebspringboot">Arrancando el Servicio Web SpringBoot:</h3>
<p>Para compilar y arrancar nuestro servicio hay varias opciones:</p>
<p><strong>1.-</strong> La forma m&#xE1;s r&#xE1;pida es desde la ventana <strong>&apos;Project&apos;</strong> seleccionando el archivo que corresponde con la clase <strong>&apos;Application&apos;</strong> de nuestro proyecto.<br>
Haciendo click derecho y seleccionando <strong>&apos;Run&apos;</strong> o pulsando <em>Ctrl+May&#xFA;s+F10</em> se compilar&#xE1; nuestro c&#xF3;digo y se arrancar&#xE1; el servicio web.<br>
Si lo que queremos es depurar, en vez de seleccionar la opci&#xF3;n <strong>&apos;Run&apos;</strong> deberemos seleccionar <strong>&apos;Debug&apos;</strong>.</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="http://www.atopecode.net/content/images/2021/03/IntelliJIdea-Community---SpringBoot-001.png" class="kg-image" alt="Arrancando servicio web SpringBoot en IntelliJ Idea Community" loading="lazy" width="1918" height="1025" srcset="http://www.atopecode.net/content/images/size/w600/2021/03/IntelliJIdea-Community---SpringBoot-001.png 600w, http://www.atopecode.net/content/images/size/w1000/2021/03/IntelliJIdea-Community---SpringBoot-001.png 1000w, http://www.atopecode.net/content/images/size/w1600/2021/03/IntelliJIdea-Community---SpringBoot-001.png 1600w, http://www.atopecode.net/content/images/2021/03/IntelliJIdea-Community---SpringBoot-001.png 1918w" sizes="(min-width: 720px) 720px"></figure><!--kg-card-begin: markdown--><p>En la ventana <strong>&apos;Run&apos;</strong> podemos ver los logs que indican que el servicio ha arrancado correctamente y desde esta misma ventana podemos finalizar su ejecuci&#xF3;n haciendo click en el bot&#xF3;n con el s&#xED;mbolo de <strong>Stop</strong> <img src="http://www.atopecode.net/content/images/2021/03/stop.png" alt="Arrancando servicio web SpringBoot en IntelliJ Idea Community" style="display:inline-block; margin:0em"></p>
<p>Si queremos volver a arrancar el servicio despu&#xE9;s de la primera vez, desde esta misma ventana de <strong>&apos;Run&apos;</strong> se puede hacer click en el bot&#xF3;n <strong>&apos;Rerun&apos;</strong> <img src="http://www.atopecode.net/content/images/2021/03/rerun.png" alt="Arrancando servicio web SpringBoot en IntelliJ Idea Community" style="display:inline-block; margin:0em">  sin tener que volver a seleccionar el archivo de nuestra clase <strong>&apos;Application&apos;</strong>.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><p><strong>2.-</strong> La opci&#xF3;n standard y que se deber&#xED;a de hacer en cada proyecto es a&#xF1;adiendo una <strong>Run/Debug Configuration</strong> como se muestra en el siguiente video:</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-embed-card"><iframe width="200" height="113" src="https://www.youtube.com/embed/vlBOgNflp_g?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen title="Arrancar proyecto SpringBoot en IntellijIdea Community"></iframe></figure><p></p><!--kg-card-begin: markdown--><p><strong>3.-</strong> Otra opci&#xF3;n es utilizando el plugin maven correspondiente desde la ventana de <strong>&apos;Maven&apos;</strong>. Localizando el plugin <strong>&apos;spring-boot - spring-boot:run&apos;</strong> y haciendo doble click se arranca nuestro servicio web.</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="http://www.atopecode.net/content/images/2021/03/IntelliJIdea-Community---SpringBoot-002.png" class="kg-image" alt="Arrancando servicio web SpringBoot en IntelliJ Idea Community" loading="lazy" width="1920" height="1037" srcset="http://www.atopecode.net/content/images/size/w600/2021/03/IntelliJIdea-Community---SpringBoot-002.png 600w, http://www.atopecode.net/content/images/size/w1000/2021/03/IntelliJIdea-Community---SpringBoot-002.png 1000w, http://www.atopecode.net/content/images/size/w1600/2021/03/IntelliJIdea-Community---SpringBoot-002.png 1600w, http://www.atopecode.net/content/images/2021/03/IntelliJIdea-Community---SpringBoot-002.png 1920w" sizes="(min-width: 720px) 720px"></figure><!--kg-card-begin: markdown--><p>Igual que el paso anterior se nos vuelve a abrir la ventana de <strong>&apos;Run&apos;</strong> desde la cual se muestran los logs y podremos finalizar la ejecuci&#xF3;n del servicio (<strong>stop</strong>) <img src="http://www.atopecode.net/content/images/2021/03/stop.png" alt="Arrancando servicio web SpringBoot en IntelliJ Idea Community" style="display:inline-block; margin:0em"> o volver a ejecutarlo (<strong>rerun</strong>) <img src="http://www.atopecode.net/content/images/2021/03/rerun.png" alt="Arrancando servicio web SpringBoot en IntelliJ Idea Community" style="display:inline-block; margin:0em"></p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><p><strong>4.-</strong> La cuarta y &#xFA;ltima forma de arrancar nuestro servicio web es la m&#xE1;s rudimentaria y menos &#xE1;gil cuando estamos desarrollando.</p>
<p>Consiste en ejecutar desde la ventana de terminal y dentro del directorio ra&#xED;z de nuestro proyecto el comando:</p>
<pre><code>./mvnw spring-boot:run</code></pre>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="http://www.atopecode.net/content/images/2021/03/IntelliJIdea-Community---SpringBoot-003.png" class="kg-image" alt="Arrancando servicio web SpringBoot en IntelliJ Idea Community" loading="lazy" width="1920" height="1043" srcset="http://www.atopecode.net/content/images/size/w600/2021/03/IntelliJIdea-Community---SpringBoot-003.png 600w, http://www.atopecode.net/content/images/size/w1000/2021/03/IntelliJIdea-Community---SpringBoot-003.png 1000w, http://www.atopecode.net/content/images/size/w1600/2021/03/IntelliJIdea-Community---SpringBoot-003.png 1600w, http://www.atopecode.net/content/images/2021/03/IntelliJIdea-Community---SpringBoot-003.png 1920w" sizes="(min-width: 720px) 720px"></figure><!--kg-card-begin: markdown--><p>Nuestro servicio web empezar&#xE1; a arrancarse y se mostraran los correspondientes logs en la ventana de <strong>&apos;Terminal&apos;</strong>.<br>
En este caso para finalizar el proceso hay que pulsar en dicha ventana las teclas <strong>&apos;Ctrl + C&apos;</strong> y teclear <strong>&apos;s&apos;</strong> cuando se nos pregunte si queremos finalizar el proceso.<br>
Para volver a ejecutar el servicio tendremos que volver a ejecutar el comando.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h3 id="conclusin">Conclusi&#xF3;n:</h3>
<p>Personalmente prefiero cualquiera de las 2 primeras opciones.<br>
Es aconsejable utilizar siempre la misma opci&#xF3;n para arrancar y detener el servicio web, ya que si cambiamos de una a otra podemos dejar el servicio en ejecuci&#xF3;n y al intentar volver a ejecutarlo nos dar&#xE1; error porque ya se est&#xE1; usando el mismo puerto.</p>
<p>Si ya se est&#xE1; acostumbrado a utilizar el Spring Tool Suite igual no es buena idea pasarse al IntelliJ Idea sin tener una licencia (cuestan sobre unos 500 euros al a&#xF1;o).<br>
Si ya se est&#xE1; utilizando el IntelliJ Idea Community y lo que quieres es hacer algo con SpringBoot entonces las opciones descritas en este art&#xED;culo si que pueden serte &#xFA;tiles.</p>
<p>Nos vemos.<br>
A tope codear!!!</p>
<!--kg-card-end: markdown--><p></p><p></p><!--kg-card-begin: html--><!--a href="https://amzn.to/3IoDnz0" target="_blank"-->
<!--img src="http://www.atopecode.net/content/images/2022/01/Regalos-programador-informatico.png" class="kg-image fluidbox__thumb" alt="Regalos programador informático" srcset="http://www.atopecode.net/content/images/size/w600/2022/01/Regalos-programador-informatico.png 600w, http://www.atopecode.net/content/images/size/w1000/2022/01/Regalos-programador-informatico.png 1000w, http://www.atopecode.net/content/images/2022/01/Regalos-programador-informatico.png 1212w" sizes="(min-width: 720px) 720px" style="opacity: 0.9;"-->
<!--/a--><!--kg-card-end: html--><p></p>]]></content:encoded></item></channel></rss>