Spring framework 5 : Definiendo un perfil por defecto


En el post anterior aprendimos como crear e utilizar perfiles utilizando spring, ahora toca el turno de aprender como definir un perfil por defecto.

Definiendo los servicios a utilizar

/**
 * 
 * @author raidentrance
 *
 */
public interface EnvironmentService {

	String getEnvironmentName();

}
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

/**
 * @author raidentrance
 *
 */
@Service
@Profile({"dev","default"})
public class DevEnvironmentService implements EnvironmentService {
	
	@Override
	public String getEnvironmentName(){
		return "dev";
	}
	
}
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

/**
 * @author raidentrance
 *
 */
@Service
@Profile("qa")
public class QaEnvironmentService implements EnvironmentService {

	@Override
	public String getEnvironmentName() {
		return "qa";
	}

}
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

/**
 * @author raidentrance
 *
 */
@Service
@Profile("prod")
public class ProdEnvironmentService implements EnvironmentService{

	@Override
	public String getEnvironmentName() {
		return "prod";
	}

}

Como vemos tenemos una interfaz llamada EnvironmentService.java la cual cuenta con 3 implementaciones DevEnvironmentService.java, QaEnvironmentService.java y ProdEnvironmentService.java, estos servicios están anotados con @Profile indicando el perfil de acuerdo al cual se crearán.

Solo tenemos una particularidad en la clase DevEnvironmentService podemos ver que la anotación se ve del siguiente modo:

.....
@Profile({"dev","default"})
public class DevEnvironmentService implements EnvironmentService {
...

Esto significa que el bean se activará con el perfil de dev o si no se especifica ningún otro perfil.

Ejecutando la aplicación

Para probar la aplicación ejecutaremos la siguiente clase:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import com.devs4j.datasource.EnvironmentService;

@SpringBootApplication
public class SpringExampleApplication {

	public static void main(String[] args) {
		ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringExampleApplication.class, args);
		EnvironmentService service = applicationContext.getBean(EnvironmentService.class);
		System.out.printf("Active environment %s \n", service.getEnvironmentName());
	}

}

Como podemos ver no especificamos ningún perfil en el archivo application.properties, y a pesar de eso al ejecutar nuestra aplicación veremos la siguiente salida:

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

2019-01-30 21:58:20.406  INFO 16889 --- [           main] com.devs4j.SpringExampleApplication      : Starting SpringExampleApplication on MacBook-Pro-de-Alejandro.local with PID 16889 (/Users/raidentrance/Documents/workspace-sts-3.8.3.RELEASE/spring-example/target/classes started by raidentrance in /Users/raidentrance/Documents/workspace-sts-3.8.3.RELEASE/spring-example)
2019-01-30 21:58:20.409  INFO 16889 --- [           main] com.devs4j.SpringExampleApplication      : No active profile set, falling back to default profiles: default
2019-01-30 21:58:21.069  INFO 16889 --- [           main] com.devs4j.SpringExampleApplication      : Started SpringExampleApplication in 1.04 seconds (JVM running for 1.438)
Active environment dev 

Esto nos muestra que el perfil que se activó fue el perfil por defecto.

Para estar al pendiente sobre nuestro contenido nuevo síguenos en nuestras redes sociales  https://www.facebook.com/devs4j/ y https://twitter.com/devs4j.

Autor: Alejandro Agapito Bautista
Twitter: @raidentrance
Contacto:raidentrance@gmail.com

Spring framework 5 : Uso de perfiles en Spring


Cuando trabajamos con Spring framework nos encontraremos con momentos en los que tenemos que activar o desactivar beans de acuerdo a ciertas circunstancias, por ejemplo diferentes ambientes utilizan diferentes bases de datos, diferentes países utilizan diferentes idiomas, etc.

Definición de servicios

Para mostrar el funcionamiento de los perfiles en Spring crearemos los siguientes servicios de spring de ejemplo:

/**
 * 
 * @author raidentrance
 *
 */
public interface EnvironmentService {

	String getEnvironmentName();

}
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

/**
 * @author raidentrance
 *
 */
@Service
@Profile("dev")
public class DevEnvironmentService implements EnvironmentService {
	
	@Override
	public String getEnvironmentName(){
		return "dev";
	}
	
}
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

/**
 * @author raidentrance
 *
 */
@Service
@Profile("qa")
public class QaEnvironmentService implements EnvironmentService {

	@Override
	public String getEnvironmentName() {
		return "qa";
	}

}

import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;

/**
 * @author raidentrance
 *
 */
@Service
@Profile("prod")
public class ProdEnvironmentService implements EnvironmentService{

	@Override
	public String getEnvironmentName() {
		return "prod";
	}

}

Como vemos tenemos una interfaz llamada EnvironmentService.java la cual cuenta con 3 implementaciones DevEnvironmentService.java, QaEnvironmentService.java y ProdEnvironmentService.java, estos servicios están anotados con @Profile indicando el perfil de acuerdo al cual se crearán.

Utilizando el Bean

Una vez que se definieron los beans el siguiente paso será utilizarlos, para esto utilizaremos el siguiente código:


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import com.devs4j.datasource.EnvironmentService;

@SpringBootApplication
public class SpringExampleApplication {

	public static void main(String[] args) {
		ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringExampleApplication.class, args);
		EnvironmentService service = applicationContext.getBean(EnvironmentService.class);
		System.out.printf("Active environment %s \n", service.getEnvironmentName());
	}

}

Al ejecutar el código anterior tendremos la siguiente salida:

Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.devs4j.datasource.EnvironmentService' available

Como vemos el error nos dice que no asignamos un qualifier al obtener la referencia de EnvironmentService, en lugar de hacerlo lo que haremos será definir un perfil de spring.

Definiendo el perfil

Como recordamos del primer post https://devs4j.com/2019/01/29/spring-framework-5-creando-un-proyecto-de-spring/ existe un archivo llamado application.properties, en el cual podremos definir el perfil bajo el cual se ejecutará nuestra aplicación, para hacerlo le agregaremos la siguiente línea:

application.properties

spring.profiles.active=dev

La línea anterior indica que el perfil que se activará será el perfil de desarrollo, una vez hecho esto ejecutaremos de nuevo nuestra aplicación y veremos lo siguiente:

2019-01-30 21:23:47.416  INFO 16798 --- [           main] com.devs4j.SpringExampleApplication      : Starting SpringExampleApplication on MacBook-Pro-de-Alejandro.local with PID 16798 (/Users/raidentrance/Documents/workspace-sts-3.8.3.RELEASE/spring-example/target/classes started by raidentrance in /Users/raidentrance/Documents/workspace-sts-3.8.3.RELEASE/spring-example)
2019-01-30 21:23:47.423  INFO 16798 --- [           main] com.devs4j.SpringExampleApplication      : The following profiles are active: dev
2019-01-30 21:23:48.158  INFO 16798 --- [           main] com.devs4j.SpringExampleApplication      : Started SpringExampleApplication in 1.253 seconds (JVM running for 1.641)
Active environment dev 

Como vemos el perfil se activó correctamente y al final nos imprime Active environment dev.

¿Qué pasa si inyecto el bean utilizando su implementación?

Como vimos activamos el perfil de dev y por esto al obtener una referencia de tipo EnvironmentService.java obtuvimos una de tipo DevEnvironmentService.java, pero, ¿Qué pasa si hacemos lo siguiente? :

ProdEnvironmentService prodService = applicationContext.getBean(ProdEnvironmentService.class);

Como vemos el perfil activo es dev, pero estamos tratando de obtener el bean del tipo ProdEnvironmentService.java. Al hacerlo nos podemos dar cuenta que obtendremos el siguiente error:

Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.devs4j.datasource.ProdEnvironmentService' available

Esto es debido a que el bean no se creó por spring y no se agregó al contexto dado que no tiene el perfil indicado.

Para estar al pendiente sobre nuestro contenido nuevo síguenos en nuestras redes sociales  https://www.facebook.com/devs4j/ y https://twitter.com/devs4j.

Autor: Alejandro Agapito Bautista
Twitter: @raidentrance
Contacto:raidentrance@gmail.com

Spring boot + Profiles (Soporta múltiples entornos)


Uno de los problemas más comunes cuando se desarrollan aplicaciones en cualquier lenguaje es utilizar un solo código que este se ejecute en múltiples entornos (dev, qa y prod). En todos los entornos se utilizarán las mismas configuraciones, lo que cambiará de uno a otro serán los valores asignados a las mismas.

Para soportar esto Spring boot provee perfiles que son diferentes a los perfiles normales de Maven, para saber más sobre ellos re recomendamos el post Perfiles Maven en Español !.

Primeros pasos

Antes de adentrarnos a cómo configurar los perfiles primero existen algunos conceptos que debemos entender:

  • Un perfil se asignará a un entorno de tal modo que si tenemos los entornos, Desarrollo, QA y Producción se deberán crear 3 perfiles.
  • A diferencia de los perfiles normales de Maven, en Spring boot los perfiles se utilizarán en tiempo de ejecución, de este modo no es necesario compilar los proyectos con la bandera -P.
  • Existen diferentes formas de definir un perfil en Maven, en este post se explicarán las 2 principales utilizando archivos properties y utilizando archivos yaml.
  • Para estos ejemplos se tomará como base el proyecto creado en el post Spring Boot + REST Jersey Parte 1.

Definiendo perfiles utilizando archivos properties

Para configurar los perfiles maven utilizando archivos properties es necesario hacer lo siguiente.

1. Agregar un archivo properties por cada perfil que se desea soportar

El primer paso será crear un archivo properties en el folder src/main/resources por cada uno de los entornos a soportar siguiendo la siguiente estructura:

application-${profile}.properties

${profile} Será el nombre de nuestro entorno. Para este ejemplo se crearán los siguientes 3 archivos:

  • application-dev.properties
 com.raidentrance.app.name=Spring boot dev
  • application-qa.properties
 com.raidentrance.app.name=Spring boot qa
  • application-prod.properties
 com.raidentrance.app.name=Spring boot prod

2. Crear un nuevo servicio para mostrar el valor de la propiedad

Una vez definida la nueva propiedad llamada com.raidentrance.app.name el siguiente paso será exponerla en un servicio. Para esto se creará el siguiente servicio:

/**
*
*/
package com.raidentrance.resource;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
* @author maagapi
*
*/
@Component
@Path("/configs")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class ProjectConfigurationResource {

@Value("${com.raidentrance.app.name}")
private String appName;

private static final Logger log = LoggerFactory.getLogger(UserResource.class);

@GET
@Path("/appName")
public Response getAppName() {
log.info("Getting project configuration");
return Response.ok(appName).build();
}
}

El servicio anterior mostrará en la respuesta del endpoint GET /configs/appName el nombre definido en nuestro archivo de configuración.

3. Registrar el servicio en Jersey

Recordemos que siempre que se crea un servicio nuevo en Jersey lo debemos agregar en nuestra clase JerseyConfig como se muestra a continuación:

/**
*
*/
package com.raidentrance.config;

import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.stereotype.Component;

import com.raidentrance.resource.ProjectConfigurationResource;
import com.raidentrance.resource.UserResource;

/**
* @author raidentrance
*
*/
@Component
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(UserResource.class);
register(ProjectConfigurationResource.class);
}
}

Recordemos que el servicio UserResource fue el que ya existía en el ejemplo y no es necesario para la explicación de perfiles.

4. Probado todo junto

Una vez que ya se configuró todo lo anterior lo único que resta es probarlo, para esto solo es necesario compilar nuestra aplicación como siempre con un simple mvn clean install, lo único que será diferente será al momento de ejecutarlo, para esto se utilizará el siguiente comando:

mvn -Dspring.profiles.active=qa spring-boot:run

Como se puede ver en el comando se ejecutará la aplicación utilizando las configuraciones definidas en el perfil de qa.

Si ejecutamos el endpoint GET /configs/appName la salida será la siguiente:

Spring boot qa

Definiendo perfiles utilizando archivos yaml

Una vez que ya sabemos como definir los perfiles utilizando multiples archivos .properties, el siguiente paso será entender como hacerlo utilizando archivos yaml.

Paso 1 Definir las configuraciones

A diferencia de la configuración utilizando archivos properties, para definir multiples perfiles utilizando archivos yaml solo es necesario crear un solo archivo para todos los perfiles de la aplicación veamos el ejemplo mencionado anteriormente pero utilizando archivos yaml.

spring:
    profiles:
        active: dev
---
spring:
    profiles: dev
com:
    raidentrance:
      app:
        name: Spring boot dev
---
spring:
    profiles: qa
com:
    raidentrance:
      app:
        name: Spring boot qa
---
spring:
    profiles: prod
com:
    raidentrance:
      app:
        name: Spring boot prod

Como se puede observar a diferencia de la definición con archivos properties es posible definir en un solo archivo yaml el perfil por defecto a utilizar y las propiedades para los diferentes entornos.

Los demás componentes del código serán iguales para ambas formas.

Puedes encontrar el código completo en el siguiente enlace https://github.com/raidentrance/spring-boot-example/tree/part7-profiles .

Si te gusta el contenido o tienes preguntas síguenos en nuestras redes sociales: https://twitter.com/geeks_mx y https://www.facebook.com/geeksJavaMexico/.

 

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com