Creación de un Starter en Spring Boot

starter-spring-boot

starter-spring-boot


En esta entrada vamos a ver paso a paso como es la creación de un starter en Spring Boot. En muchas ocaciones, en nuestros proyectos, nos vemos con la necesidad de crear librerías que sean transversales para nuestras aplicaciones Spring Boot. Para lograr simplificar este trabajo y su desarrollo hacemos uso de los Starters de Spring Boot.

Los starters nos van a permitir cargar configuraciones e inyectar beans en nuestras aplicaciones únicamente con añadir la dependencia que hemos creado con la funcionalidad.

Un ejemplo muy normal y común para el que se suele crear un starter de Spring Boot, es cuando tenemos un cliente web (WebClient, RestTemplate etc), y queremos añadir una configuración segura SSL para la comunicación. En este caso lo más normal es crear un starter en el que le inyectamos esa configuración cada vez que se use.

¿Cómo funciona un Starter de Spring Boot?

Antes de empezar la creación de un starter vamos ver como es su funcionamiento viendo un starter ya existente de Spring Boot.

Configuración Starter Spring Boot

Para comenzar vamos a hablar del spring.factories, es es un fichero que se encuentra dentro de la carpeta META-INF del proyecto para generar el starter. En este fichero se encontrará la llamada a las clases de autoconfiguración de Spring que será el que lanzé nuestro Spring Boot Starter. Vamos a ver el proyecto de Spring Boot autoconfiguration para entenderlo mejor.

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\

En el fichero anterior, vemos parte de las clases de autoconfiguración que Spring Boot nos proporciona. Esas clases que vemos serán las responsables de inyectar en el proyecto que llame al Starter la configuración necesaria. Pero para ello y que no se añada esa autoconfiguración siempre habrá que analizar si hemos añadido la dependencia necesaria, es decir, si por ejemplo hemos añadido la dependencia de cache o de Rabbit, para ello se hace uso de los condicionales:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(CacheManager.class)
@ConditionalOnBean(CacheAspectSupport.class)
@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")
@EnableConfigurationProperties(CacheProperties.class)
@AutoConfigureAfter({ CouchbaseDataAutoConfiguration.class, HazelcastAutoConfiguration.class,
		HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class })
@Import({ CacheConfigurationImportSelector.class, CacheManagerEntityManagerFactoryDependsOnPostProcessor.class })
public class CacheAutoConfiguration {

En el anterior fragmento de la clase de autoconfiguración de la Cache, vemos que solo se creará si tenemos en el classpath la clase de CacheManager.

Configuración de Propiedades para Starter en Spring Boot

Como hemos visto en el fragmento de código anterior también tenemos la anotación @EnableConfigurationProperties, la cual nos cargará las propiedades de nuestro fichero de configuración que necesitamos para nuestro Starter. La clase encargada de generar las propiedades es CacheProperties.

@ConfigurationProperties(prefix="spring.cache")
public class CacheProperties

Estos 3 pasos que hemos definido, serán los imprescindibles para poder realizar la creación de un starter en Spring Boot.

Hands on: Crear un Starter en Spring Boot

A continuación vamos a ir viendo los pasos necesarios para crear un starter en Spring Boot. Para realizar nuestro ejemplo crearemos un Starter el cual, a través de una anotación, mostraremos un log con lo que tarda un método en ejecutarse. Para ello además, tendremos un fichero de propiedades en el que podremos realizar la activación del proceso.

Para crear este Starter crearemos una anotación llamada @TimeLogging y haremos uso de AOP para su implementación.

Para ver el ejemplo completo lo puedes descargar de aquí.

Dependendencias Maven

Para crear nuestro Custom Starter en Spring Boot vamos a hacer uso de la dependencia de autoconfigure:

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-autoconfigure</artifactId>
		</dependency>

Esta dependencia será necesaria para que la autoconfiguración de nuestro starter funcione.

Localización de las clases de Autoconfiguración

Para que Spring detecte nuestra clase de Autoconfiguración, es necesaria definirla en un fichero. Esta clase será la encargada de lanzar todo el proceso de autoconfiguración.

Para poder cargar la autoconfiguración vamos a añadir un fichero llamado spring.factories en la carpeta META-INF del proyecto que acabamos de generar, la cual sino existe la creamos dentro de la carpeta Resources

En este fichero vamos a configurar nuestro @Bean que creará la instancia de intercepción de cabeceras:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.refactorizando.example.starter.time.logging.TimeLoggingAutoconfigure

Creación de la clase de Autoconfiguración en un Starter de Spring Boot

Para poder crear un Starter de Spring Boot uno de los pasos más importantes es crear nuestra clase de autoconfiguración que se encargará de crear un @Bean dentro del proyecto. En nuestro caso nuestra clase de Autoconfiguración se encargará de crear el Bean responsable de ejecutar el log, cargar y activar la clase si en las propiedades se encuentra activo.

@Configuration
@ConditionalOnClass(TimeLoggingAspect.class)
@EnableConfigurationProperties(LoggingProperties.class)
public class TimeLoggingAutoconfigure {

    private final LoggingProperties properties;

    public TimeLoggingAutoconfigure(LoggingProperties properties) {
        this.properties = properties;
    }

    @Bean
    public TimeLoggingAspect loggableAspect(){
        return new TimeLoggingAspect(properties.getLoggerName());
    }
}

En nuestra clase de Autoconfiguración vamos a cargar el Bean que es un aspecto que se encarga (siempre que tenga la anotación @TimeLogging) de procesar el tiempo que tardará un método en procesarse.

Además hemos añadido un ConditionalOnClass el cual no cargará ninguna configuración si nuestra clase TimeLoggingAspect no se encuentra en el classpath.

Finalmente vamos a ver nuestra clase Aspect que se encarga de calcular el tiempo total que tardará en procesarse nuestro método.

Creación del Aspecto para calcular tiempo de ejecución de un método

El objetivo de nuestro starter es poder calcular el tiempo de ejecución que un método tardará en procesarse. Para ello vamos a hacer uso de AOP para poder interceptar un método y calcular su tiempo cuando finalice. Para ello haremos uso de la dependencia:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

Y mediante la anotación @Aspect indicaremos que vamos a interceptar una petición:

@Aspect
public class TimeLoggingAspect {

  private final Logger log;

  public TimeLoggingAspect(String loggerName) {
    log = LoggerFactory.getLogger(loggerName);
  }

  @Pointcut("@annotation(com.vishnu.service.TrackTime)")
  public void executeTiming() {
  }

  @Around("executeTiming()")
  public Object logMethodTiming(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

    long startTime = System.currentTimeMillis();

    Object returnValue = proceedingJoinPoint.proceed();

    long totalTime = System.currentTimeMillis() - startTime;

    log.info("Time Taken by service '{}' is {} ms", proceedingJoinPoint.getSignature().getName(),
        totalTime);
    
    return returnValue;
  }
}

Para calcular el tiempo de procesamiento hemos hecho uso de Around. Esta funcionalidad nos va a permitir añadir algún comportamiento antes o después de la ejecución. Además podemos hacer uso de la clase ProceedingJoingPoint para obtener información o devolver una excepción o realizar algún cambio al devolverlo.

Probar el starter creado

Vamos a probar nuestro starter, para ello hemos creado una simple aplicación en la que poder ver nuestra funcionalidad. La aplicación la puedes descargar de nuestro github, aunque previamente necesitarás generar el starter que hemos creado para ello podrás descargarlo de aquí y ejecutar el siguiente comando:

mvn clean install

Una vez hemos generado nuestro propio Spring Boot Starter, podremos ejecutar nuestra aplicación con:

mvn spring-boot:run

Para que nuestra aplicación tenga la funcionalidad que hemos creado será necesario añadir la siguiente dependencia, con la que podrá calcular los métodos.

        <dependency>
            <groupId>com.refactorizando.example</groupId>
            <artifactId>spring-boot-starter-methog-logging-time</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

Además será necesario añadir en nuestro fichero de propiedades:

logging.api.enabled=true

Una vez arrancamos nuestra aplicación ejecutamos http://localhost:8080 y veremos el tiempo que ha tardado nuestro método en procesarlo.

Creación de un Starter en Spring Boot
Creación de un Starter en Spring Boot

Conclusión

En esta entrada hemos visto la creación de un Starter en Spring Boot para poder crear librerías transversales a nuestros proyectos. La creación de starters en Spring Boot facilitarán el trabajo de desarrollo cuando se tienen que crear muchos microservicios con iguales o similares características. De esta manera podemos simplificar el trabajo y el tiempo invertido en el desarrollo de nuevos servicios.

Si quieres echar un vistazo al ejemplo completo lo puedes ver aquí.

Si necesitas más información puedes escribirnos un comentario o un correo electrónico a refactorizando.web@gmail.com o también nos puedes contactar por nuestras redes sociales Facebook o twitter y te ayudaremos encantados!


Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *