1. Introducción

Hola a todos!. En este post veremos cómo inyectar propiedades dentro de nuestros microservicios de Spring Boot. Éstos se definen dentro del fichero application.yml que se debe encontrar dentro del classpath de la aplicación. En futuros post veremos que la solución mas realista es situar los properties fuera del classpath para poder realizar cambios de propiedades sin necesidad de realizar un build. Esto lo resolverá el servidor de Configuration de Spring Cloud.

Básicamente existen tres maneras de inyectar propiedades dentro de nuestros beans de Spring Boot.

  • Enviroment
  • @Value
  • @ConfigurationProperties

Os podéis descargar el código de ejemplo de mi GitHub aquí.

Tecnologías empleadas:

  • Java 8
  • Gradle 3.1
  • SpringBoot 1.5.2.RELEASE
  • Spring 4.3.7.RELEASE

2. Ficheros

group 'com.jorgehernandezramirez.spring.springboot'
version '1.0-SNAPSHOT'

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.2.RELEASE")
    }
}

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
apply plugin: 'maven'

sourceCompatibility = 1.8

springBoot {
    mainClass = "com.jorgehernandezramirez.spring.springboot.helloworld.Application"
}

repositories {
    mavenCentral()
}

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web")
}

Mostramos la clase Application.java que permitirá arrancar Spring Boot.

package com.jorgehernandezramirez.spring.springboot.property;

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

@SpringBootApplication
public class Application {

    public Application(){
        //For Spring
    }

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

Nos creamos los siguientes properties de ejemplo dentro del fichero application.yml. Se definen cuáles son las diferentes base de datos que soporta springdata así como algunas características de ellas como su nombre, si son relacionales y sus fundadores. Fijaros que para especificar una lista de elementos en el caso de los fundadores, debemos poner el caracter - delante de cada elementos de la lista.

spring:
  springdata:
    mongodb:
      relational: false
      name: mongodb
      founders:
      - "Kevin P. Ryan"
      - "Eliot Horowitz"
      - "Dwight Merriman"
    oracle:
      relational: true
      name: oracle
      founders:
      - "Lawrence J. Ellison"
      - "Ed Oates"
      - "Bob Miner"

A continuación veremos las diferentes formas que tenemos de hacer uso de estos properties dentro de nuestra aplicación.

3. Enviroment

Es la menos utilizada. Consiste en hacer uso de la clase org.springframework.core.env.Enviroment de Spring. Para obtener el nombre de la base de datos de mongodb:

@Autowired
private Environment environment;
...
@RequestMapping("/mongodb/name/enviroment")
public String getMongoDbNameUsingEnviroment(){
   return environment.getProperty("spring.springdata.mongodb.name");
}

4. @Value

Consiste en hacer uso de la anotación @Value de Spring. Para obtener el nombre de la base de datos de mongodb:

@Value("${spring.springdata.mongodb.name}")
private String mongoDBName;
...
@RequestMapping("/mongodb/name/value")
public String getMongoDbNameUsingValue(){
   return mongoDBName;
}

5. @Configurationproperties

Es el mecanismo más potente y más utilizado. Consiste en realizar el mapeo de propiedades sobre una clase java. Si queremos mapear las propiedades de la base de datos mongodb.

package com.jorgehernandezramirez.spring.springboot.property.configuration;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import java.io.Serializable;
import java.util.List;

@Configuration
@ConfigurationProperties("spring.springdata.mongodb")
public class SpringDataConfiguration {

    private Boolean relational;

    private String name;

    private List<String> founders;

    public SpringDataConfiguration(){
        //For Spring
    }

    public SpringDataConfiguration(Boolean relational, String name, List<String> founders) {
        this.relational = relational;
        this.name = name;
        this.founders = founders;
    }

    public Boolean getRelational() {
        return relational;
    }

    public void setRelational(Boolean relational) {
        this.relational = relational;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<String> getFounders() {
        return founders;
    }

    public void setFounders(List<String> founders) {
        this.founders = founders;
    }

    @Override
    public String toString() {
        return "MongoDBConfiguration{" +
                "relational=" + relational +
                ", name='" + name + '\'' +
                ", founders='" + founders + '\'' +
                '}';
    }
}

Para hacer uso de dicha clase basta con inyectarla dentro de algún bean de Spring.

@Autowired
private SpringDataConfiguration mongoDBConfiguration;
...
@RequestMapping("/mongodb/name/configurationproperties")
public String getMongoDbNameUsingConfigurationProperties(){
   return mongoDBConfiguration.getName();
}

Realizar mapeo de todos las bases de datos definidas en application.yml en un mapa

Lo que vamos a hacer a continuación es mapear todas las bases de datos definidas en el fichero de propiedades en un Map, siendo la clave la base de datos (mongodb, oracle) y el valor una instancia de SpringDataConfiguration. Además tener en cuenta

  • En la anotación @ConfigurationProperties especificamos la ruta yml desde donde se va a empezar a mapear los datos
  • El atributo springdata de la clase SpringDatasConfiguration debe coincidir con la subpropiedad que se debe encontrar por debajo de spring: dentro de application.yml
package com.jorgehernandezramirez.spring.springboot.property.configuration;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import java.util.Map;

@Configuration
@ConfigurationProperties("spring")
public class SpringDatasConfiguration {

    private Map<String, SpringDataConfiguration> springdata;

    public SpringDatasConfiguration(){
        //For Spring
    }

    public SpringDatasConfiguration(Map<String, SpringDataConfiguration> springdata) {
        this.springdata = springdata;
    }

    public Map<String, SpringDataConfiguration> getSpringdata() {
        return springdata;
    }

    public void setSpringdata(Map<String, SpringDataConfiguration> springdata) {
        this.springdata = springdata;
    }
}

Para hacer uso de esta clase basta con inyectarla dentro de un bean de Spring.

@Autowired
private SpringDatasConfiguration springDatasConfiguration;
...
@RequestMapping("/")
public Map<String, SpringDataConfiguration> getAllConfigurationUsingConfigurationProperties(){
   return springDatasConfiguration.getSpringdata();
}

A continuación mostramos la clase PropertyController.java entera

package com.jorgehernandezramirez.spring.springboot.property.controller;

import com.jorgehernandezramirez.spring.springboot.property.configuration.SpringDataConfiguration;
import com.jorgehernandezramirez.spring.springboot.property.configuration.SpringDatasConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
@RequestMapping(value = "/property")
public class PropertyController {

    @Value("${spring.springdata.mongodb.name}")
    private String mongoDBName;

    @Autowired
    private Environment environment;

    @Autowired
    private SpringDataConfiguration mongoDBConfiguration;

    @Autowired
    private SpringDatasConfiguration springDatasConfiguration;

    public PropertyController(){
        //For Spring
    }

    @RequestMapping("/mongodb/name/value")
    public String getMongoDbNameUsingValue(){
        return mongoDBName;
    }

    @RequestMapping("/mongodb/name/enviroment")
    public String getMongoDbNameUsingEnviroment(){
        return environment.getProperty("spring.springdata.mongodb.name");
    }

    @RequestMapping("/mongodb/name/configurationproperties")
    public String getMongoDbNameUsingConfigurationProperties(){
        return mongoDBConfiguration.getName();
    }

    @RequestMapping("/")
    public Map<String, SpringDataConfiguration> getAllConfigurationUsingConfigurationProperties(){
        return springDatasConfiguration.getSpringdata();
    }
}

6. Probando la aplicación

Compilamos y ejecutamos la aplicación

gradle build
java -jar PATH_TO_JAR

Ejecutamos los controladores

curl http://localhost:8080/property/mongodb/name/value

La respuesta obtenida es

mongodb

curl http://localhost:8080/property/mongodb/name/enviroment

La respuesta obtenida es

mongodb

curl http://localhost:8080/property/mongodb/name/configurationproperties

La respuesta obtenida es

mongodb

curl -H "Accept:application/json" http://localhost:8080/property/

La respuesta obtenida es

{“mongodb”:{“relational”:false,”name”:”mongodb”,”founders”:[“Kevin P. Ryan”,”Eliot Horowitz”,”Dwight Merriman”]},”oracle”:{“relational”:true,”name”:”oracle”,”founders”:[“Lawrence J. Ellison”,”Ed Oates”,”Bob Miner”]}}