Etiqueta: Framework

  • Spring Boot – Configuración

    Spring Boot – Configuración

    Vimos anteriormente que se puede hacer la inyección de dependencias por medio de archivos xml y usando anotaciones; sin embargo también podemos usar clases de configuración para instanciar objetos de manera fácil y limpia.

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
     
    @Configuration
    public class AppConfig {
         
        @Bean("student")
        public Student getStudent(Service service) {
            return new StudentImpl(service);
        }
         
        @Bean("service")
        public Service getService() {
            return new ServiceImpl();
        }
    }

    La ventaja de este método es que permite liberar a las clases de negocio de anotaciones, dejándolas limpias.

    La clave es marcar la clase con el decorador @Configuration, es cual es procesado por el Spring IoC, que en este caso crea las instancias marcadas con @Bean.

    Veamos un ejemplo muy básico:

    1. Creamos una interfaz con un método de prueba:
      public interface Service {
          String hello();
      }
    2. Creamos la implementación del servicio:
      public class ServiceImpl implements Service {
          @Override
          public String hello() {
              return "Hola";
          }
      }
    3. Creamos el archivo de configuración:
      @Configuration
      public class Config {
          @Bean("service")
          public Service getService() {
              Service myService = new ServiceImpl();
              System.out.println(myService.hello());
              return myService;
          }
      }

      Lo que hacemos es instanciar la implementación e imprimir en consola el String que viene del método hello, lo que resulta que al ejecutar la aplicación veamos un “hola” en consola.

        .   ____          _            __ _ _
       /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
      ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
       \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
        '  |____| .__|_| |_|_| |_\__, | / / / /
       =========|_|==============|___/=/_/_/_/
       :: Spring Boot ::                (v2.7.0)
      
      Hola

    Al ser una clase Java obtenemos las ventajas propias del lenguaje, como asegurarnos del tipado correcto, comprobación en tiempo de compilación y algo mas amigable para la lectura.

    También si se desea, podemos usar las anotaciones en conjunto con con las clases de configuración.

  • Spring Boot – Anotaciones

    Spring Boot – Anotaciones

    Segunda parte del curso Spring Boot

    En el anterior post hablamos de los conceptos básicos, y este es uno de los principales, pero merece un espacio propio.

    Tocamos temas sobre inyección de dependencias y beans, sin embargo no hablamos del ciclo de vida de estos componentes, por lo que vamos a empezar por ahí.

    Teníamos nuestro archivo XML para inicializar los beans y dijimos que podemos inicializar varios; en muchos casos estos beans dependen de otros, es decir que debemos inyectar las dependencias y comportamiento. Esto lo logramos usando la interfaz de BeanPostProcessor que nos permite crear objetos y asignar comportamientos antes y después de crear los beans con los metodospostProcessBeforeInitialization y postProcessAfterInitialization respectivamente. Lo que hace Spring es justamente en los anteriores métodos por medio de Reflexion instanciar los objetos necesario y asignar el comportamiento deseado. A partir de Spring 3.5 se implemento la autodetección de beans, es decir que ya no es necesario usar un XML para inyectar cada uno de nuestros beans. Esto se logra a través de las anotaciones.

    ¿Qué es una anotación?

    Una anotación es un metadato que se incrusta en el código, se caracterizan por iniciar con el carácter @, este no cambia el comportamiento del código pero si puede indicarle al compilador acciones especificas de lectura o ejecución, por ejemplo, Java por defecto tiene varias anotaciones, una de las mas conocidas es @Override que se utiliza para asegurar que un método heredado se sobrescribe. Así como ésta hay muchas mas y además se pueden crear anotaciones propias.

    Spring se vale de anotaciones para indicarte la la interfaz BeanPostProcessor como instanciar los beans. En el siguiente ejemplo busca la anotación @MyAnnotation:

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Method[] methods = bean.getClass().getDeclareMethods();
        for (Method get Bean: methods) {
            if (method.getAnnotation(MyAnnotation.class) != null) {
                Class[] args = method.getParameterTypes();
                try {
                    Object obj = context.getBean(args[0]);
                    method.invoke(bean, obj);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    Spring provee unas anotaciones por defecto, vamos a ver las principales.

    Anotaciones y componentes

    @Component

    Esta anotación le indica a Spring Boot que debe tratar la clase con el decorador como un bean, es decir que el propio motor se encargará de hacer la inyección.

    import org.springframework.stereotype.Component;
    
    @Component
    public class MyClass {

    @Repository, @Service y @Controller son especializaciones de @Component.

    @Repository

    Se utiliza para la capa de persistencia, en otras palabras, clases que interactúan con la base de datos. Tiene la particularidad de que tiene un control de errores mas especifico para interactuar con la sintaxis de base de datos, causando un mejor manejo de estos objetos.

    @Service

    Este se utiliza en la capa de servicio, es decir que se espera que sea lógica de negocio.

    @Controller

    Esta anotación nos enlaza con la capa de presentación, allí solemos colocar las urls que se exponen vía web.

    @Controller
    @RequestMapping("students")
    public class StudentController {
    
        @GetMapping("/{id}", produces = "application/json")
        public @ResponseBody Student getStudent(@PathVariable int id) {
            return studentService.getStudentById(id);
        }
    }

    Esta anotación se suele usar mucho en los servicios web junto con la anotación @ResponseBody que permite mapear nuestra respuesta a un objeto JSON.

    Ya que este tipo de comportamiento es el mas común, desde la versión 4.0 de Spring se creo una nueva anotación que los incluye a ambos:

    @RestController

    @RestController
    @RequestMapping("students")
    public class StudentController {
    
        @GetMapping("/{id}", produces = "application/json")
        public Student getStudent(@PathVariable int id) {
            return studentService.getStudentById(id);
        }
    }

    @Autowired

    Esta anotación nos sirve para indicarle que vamos a inyectar un bean dentro de otro, es decir como una propiedad o también lo podemos usar en los métodos set; esto quiere decir que podemos darle el control al framework para que cree la instancia de los componentes, esto es importante ya que solo podemos utilizar esta anotación para inicializar componentes definidos en el contexto de Spring, es decir, que tengan el decorador @Component o alguna de sus especializaciones.

    En el siguiente ejemplo se muestra la instanciación de un servicio como propiedad de otro bean(controlador):

    @Service
    public class StudentService {
    @RestController
    @RequestMapping("students")
    public class StudentController {
    
        @Autowired
        private StudentService studentService;
    
        @GetMapping("/{id}", produces = "application/json")
        public Student getStudent(@PathVariable int id) {
            return studentService.getStudentById(id);
        }
    }

    Con esta anotación nos surge un problema que vamos a ilustrar a continuación:

    @Service
    public class StudentService implements PersonService {
    @Service
    public class TeacherService implements PersonService {

    La idea es que queremos inicializar una instancia de la interfaz PersonService, en este caso hay dos posibles orígenes y @Autowired no sabría cual implementar, para solucionar esto podemos usar la anotación @Qualifier de la siguiente manera:

    @Service
    @Qualifier("student")
    public class StudentService implements PersonService {
    @Service
    @Qualifier("teacher")
    public class TeacherService implements PersonService {
    @RestController
    @RequestMapping("registration")
    public class RegistrationController {
    
        @Autowired
        @Qualifier("student")
        private PersonService personService;
    
        @GetMapping("/info/{id}", produces = "application/json")
        public Person getDataPerson(@PathVariable int id) {
            return personService.getBasicDataById(id);
        }
    }

    De esta manera podemos indicarle específicamente que tipo implementar. En el anterior ejemplo obtenemos la instancia del servicio StudentService.

    @Value

    Esta anotación nos sirve para inyectar valores en los campos de un constructor o método. En el siguiente ejemplo se obtiene un valor del archivo .properties y se le asigna a una variable. Podemos ver el @Value asignado a diferentes tipos de datos e incluso establecemos un valor por defecto.

    @Service
    @Qualifier("teacher")
    public class TeacherService implements PersonService {
    
        @Value("${university.class.code:default}")
        private String code;
    
        @Value("${university.class.subjects}")
    private String[] subjects;

    @Scope

    El Scope o ámbito indica el alcance de una instancia, en Spring tenemos por defecto la instancia de los beans como singleton (se crea una única instancia del beans, es decir que las siguientes instancias del mismo bean hacen referencia al único creado); como se mencionó por defecto funciona así, aunque si queremos especificarlo se hace de la siguiente manera:

    @Bean
    @Scope("singleton")
    public University university() {
        return new University();
    }

    En el anterior post también mencionamos que se pueden establecer los beans por medio de xml, para ese caso también podemos definir el scope.

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="personService" class="com.jhontona.service.PersonServiceImpl" scope="singleton" />
        <!--También podemos usar singleton="true" -->
    </beans>

    También podemos definir el alcance como prototype, el cual crea una instancia nueva en cada llamado.

    @Bean
    @Scope("prototype")
    public Teacher teacher() {
        return new Teacher();
    }

    Los anteriores scopes se pueden usar en cualquier aplicación de Spring, pero además tenemos scopes específicos para el ámbito web(Web Aware).

    El scope de Request se utiliza para crear una instancia nueva de bean por cada petición http, es decir que su uso mas común es con un @RestController aunque también lo podemos definir para un @Bean.

    @Bean
    @Scope(value = WebApplicationContext.SCOPE_REQUEST)
    public ExampleBean exampleBean() {
        return new ExampleBean();
    }
    @RestController
    @RequestScope
    public class ExampleController {
    
        @GetMapping("/test")
        public String sayHello() {
                return "Hello";
        }
    
    }

    El scope de Session se utiliza para crear una instancia nueva de bean por cada sesión http

    @Bean
    @Scope(value = WebApplicationContext.SCOPE_SESSION)
    public ExampleBean exampleBean() {
        return new ExampleBean();
    }
    @RestController
    @SessionScope
    public class ExampleController {
    
        @GetMapping("/test")
        public String sayHello() {
                return "Hello";
        }
    
    }

    El scope de Global Session se utiliza para crear una instancia nueva de bean por cada sesión http, pero este va orientado a Portlets, es decir aplicaciones que trabajan con módulos reutilizables de este tipo; las características principales de este tipo de aplicación es que dependen de un contenedor especializado en controlar estos módulos, además de que cada pagina puede estar constituida por múltiples portlets y cada una de estos tiene un request y response independiente.

    @Bean
    @Scope(value = WebApplicationContext.SCOPE_GLOBAL_SESSION)
    public ExampleBean exampleBean() {
        return new ExampleBean();
    }

    El scope de Application se utiliza para crear una instancia nueva de bean por cada ServletContext, que por lo general en una aplicación es único(aunque podemos crear mas), comportándose como un singleton.

    @Bean
    @Scope(value = WebApplicationContext.SCOPE_APPLICATION)
    public ExampleBean exampleBean() {
        return new ExampleBean();
    }
    @RestController
    @ApplicationScope
    public class ExampleController {
    
        @GetMapping("/test")
        public String sayHello() {
                return "Hello";
        }
    
    }

    El scope de WebSocket se utiliza para crear una instancia nueva de bean por cada websocket.

    @Bean
    @Scope(scopeName = "websocket")
    public ExampleBean exampleBean() {
        return new ExampleBean();
    }

    Tenemos un montón de anotaciones, pero las que hemos visto son las de uso mas frecuente o nos ayudan a entender los comportamientos por defecto que toma una aplicación en Spring (si se quiere profundizar mas en estas anotaciones le recomiendo este recurso).

    Anotaciones Javax

    Con las anotaciones propias de Spring tenemos para adaptarnos a una gran variedad de situaciones, sin embargo podemos trabajar con anotaciones propias del lenguaje o provistas por paquetes externos, por ejemplo, Javax Annotation API tiene unas anotaciones interesantes que se integran con Spring.

    @PostConstruct and @PreDestroy nos sirve para llamar un método después de la instanciación del bean o antes de su destrucción, es decir que funciona como un callback.

    import javax.annotation.*;
    
    public class ExampleController {
    
        public String sayHello() {
                return "Hello";
        }
    
        @PostConstruct
        public void init(){
            System.out.println("El Bean se acaba de inicializar.");
        }
       
        @PreDestroy
        public void destroy(){
            System.out.println("El Bean se va a destruir.");
        }
    
    }

    Contamos con otras etiquetas como @Resource e @Inject que se comporta de manera muy similar al @Autowired por lo que teniendo alternativas en Spring es mas practico. Simplemente debemos saber que podemos crear o insertar anotaciones desde otros paquetes.

    Hasta aquí llega este post, espero tus preguntas, comentarios y que compartas este contenido.

  • Spring Boot – CORE

    Spring Boot – CORE

    ¿Qué es Spring Boot?

    Es un framework de código abierto para desarrollo de aplicaciones para JVM, es decir que trabajamos principalmente con Java; sin embargo, también provee soporte para lenguajes como Groovy y Kotlin.

    Este framework nos permite implementar diversas arquitecturas y estilos de aplicación dependiendo de la necesidad del proyecto. Trabaja con versiones de JDK superiores al 8. A demás nos provee diferentes entornos de trabajo:

    • Despliegue en un servidor propio
    • Despliegue en servidor embebido
    • Despliegue en la nube

    Dentro de estos entornos nos provee una estructura modular, es decir que iremos conectando las diferentes piezas que requiera nuestra aplicación.

    Trabajando con Spring Boot

    Spring Boot nació como respuesta a las exigencias de los estándares de la comunidad de desarrollo, por este motivo intrínsecamente implementa estándares y buenas practicas, al  igual que nos propone la siguiente filosofía de diseño:

    1. Establecer altos estándares de calidad para el código.
    2. Preocúpese por el diseño de la API.
    3. Mantenga compatibilidad con versiones anteriores.

    Estas reglas las ofrece de manera automática, es decir que se implementan casi al instante, esto se logra a través de, como mencionamos antes, conectar diferentes módulos(inyección de dependencias). A demás implementa estas reglas en su propio desarrollo a partir de ser un framework que ofrece opciones en todos los niveles de aplicación y se acomoda a las diversas necesidades.

    Los módulos de Spring Boot están orientados de tal manera que podamos identificar tres capas principales:

    • Capa de presentación.
    • Capa de Negocio.
    • Capa de persistencia.

    Esta forma de trabajo modular es la que lo convierte en uno de los mas populares frameworks para Java, a demás de otros subsistemas de los cuales no hemos mencionado, como : logging, web services, seguridad, automatización de pruebas, entre otros.

    Spring Boot permite a los desarrolladores enfocarse mas en la lógica de negocio y menos en problemas de plomería.

    Conceptos claves

    Inyección de dependencias

    La inyección de dependencias es un patrón de diseño en donde se le suministran objetos a una clase en lugar de ser la propia clase quien los crea.

    Bean

    El Core de Spring Boot es un contenedor que se encarga de orquestar beans(objetos gestionados). Estos beans son objetos de tipo POJO, es decir que son clases simples con sus atributos privados a los cuales se pueden acceder por métodos get y set.

    Cumpliendo con el patrón de inyección de dependencias, proveemos estos beans a los módulos de Spring Boot indicándole su forma de instanciación y correlaciones entre ellos; el Core se encargara de implementarlo.

    Inversión del control

    Este principio de software es uno de los pilares de Spring Boot, consiste en darte el control de los objetos al propio framework, es decir que el framework será el encargado de instanciar nuestras clases y reproducir el comportamiento que deseamos a partir de la configuración que le seteamos.

    Las principales ventajas de este principio es que logra modularizar nuestra aplicación permitiendo tener nuestra lógica compacta y aislada, repercutiendo en la mantenibilidad y facilidad de implementación de pruebas.

    Para implementar este principio(IoC, Inversion of Control) se utiliza en Spring Boot la inyección de dependencias, anteriormente ya lo habíamos mencionado, para ver la diferencia vamos a plantear un ejemplo:

    Código tradicional

    public class Person {
        private Document document;
        
        public Person() {
            document = new Document();
        }
    }

    Inyección de dependencias

    public class Person {
        private Document document;
        
        public Person(Document document) {
            this.document = document;
        }
    }
    ApplicationContext

    El ApplicationContext es una interfaz que nos permite implementar IoC, es decir, la instanciación  y el control del ciclo de vida e interacción entre objetos. Se encarga de procesar nuestros beans.

    Spring provee diferentes implementaciones de esta interfaz:

    • ClassPathXmlApplicationContext
    • FileSystemXmlApplicationContext
    • WebApplicationContext

    El proceso de implementacion de, por ejemplo, ClassPathXmlApplicationContext es:

    1. Crear archivo de configuración XML(/src/main/resources/beans.xml) para los beans.
      <?xml version="1.0" encoding="UTF-8"?>
      <beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
      
          <bean id="personService" class="com.jhontona.service.PersonServiceImpl" />
      </beans>
    2. Implementar el ApplicationContext, en este caso en las pruebas unitarias(/src/test/java/com/person/service/PersonServiceTest.java)
      package com.person.service;
      import static org.junit.jupiter.api.Assertions.assertNotNull;
      
      import org.junit.jupiter.api.BeforeEach;
      import org.junit.jupiter.api.Test;
      import org.springframework.context.support.ClassPathXmlApplicationContet;
      
      public class PersonServiceTest {
          private ClassPathXmlApplicationContext context;
          private PersonService service;
      
          @BeforeEach
          void setUp() {
              context = new ClassPathXmlApplicationContext("beans.xml");
              service = context.getBean("personService", PersonService.class);
          }
      
          @Test
          void testGetOnePerson() {
              assertNotNull(service);
          }
      }

    Este archivo de beans.xml podemos personalizarlo dependiendo de nuestras necesidades, por ejemplo, podemos colocarle uno o varios alias a nuestro bean:

    <bean id="personService" name="alias1,alias2" class="com.jhontona.service.PersonServiceImpl" />

    Al bean le podemos indicar acceder a un constructor especifico o un método setter, ejemplo:

    Constructor

    public PersonService(String name, Integer age, Boolean document) {
    <bean id="personService" class="com.jhontona.service.PersonServiceImpl">
        <constructor-arg name="document" value="true"/>
        <constructor-arg index="0" value="Juanito"/>
        <constructor-arg value="19"/>
    </bean>

    Estos atributos los podemos pasar por nombre, índice, tipo u orden de los argumentos.

    A demás, podemos tener tantos beans como queramos. y hacer referencia entre ellos con el atributo ref.

    Hasta aquí esta breve introducción a Spring Boot, vimos algunos puntos claves a cerca del ecosistema de Spring.

¡Hola a todos los entusiastas de la tecnología! Quería informarles que en mi blog utilizamos cookies para mejorar la experiencia de usuario. Estas pequeñas herramientas nos ayudan a personalizar el contenido y ofrecer funciones específicas. Al continuar explorando el sitio, aceptas nuestro uso de cookies. Puedes obtener más información sobre cómo las utilizamos en nuestra política de privacidad. ¡Gracias por ser parte de esta comunidad tecnológica! 🍪    Más información
Privacidad