Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to load an external configuration with Spring Boot?

I'm currently learning how to work with Spring Boot. Until now I never used Frameworks like Spring and used files directly (FileInputStream, etc.)

So here is the case: I have some dynamic configuration values like OAuth tokens. I want to use them inside of my application but I have no clue how to realize this with Spring.

Here is some code to make clear what I'm searching for:

@Config("app.yaml")
public class Test {
    @Value("app.token")
    private String token;
    private IClient client;

    public Test(String token) {
        this.client = ClientFactory.build(token).login();
    }
}

Sure, this example is very plain. Here I want to get the value "token" dynamically from a YAML configuration file. This file must be accessible for the user and not included in the JAR file.

I also found that doc: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html but I have now idea how to apply this to my project.

How can I achive this? Thank you in advance :)

Edit:

Here are some parts of my code:

WatchdogBootstrap.java

package de.onkelmorph.watchdog;

import org.springframework.boot.Banner.Mode;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;

@SpringBootApplication
@ImportResource("classpath:Beans.xml")
public class WatchdogBootstrap {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(WatchdogBeans.class);
        app.setBannerMode(Mode.OFF);
        app.setWebEnvironment(false);
        app.run(args);
    }
}

Beans.xml (Located in default package)

<?xml version = "1.0" encoding = "UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <context:annotation-config></context:annotation-config>
</beans>

Watchdog.java

package de.onkelmorph.watchdog;

// Imports ...

@Component
@PropertySource("file:/watchdog.yml")
public class Watchdog {
    // ...

    // Configuration
    @Value("${watchdog.token}")
    private String token;

    public Watchdog() {
        System.out.println(this.token);
        System.exit(0);
    }

    // ...
}

watchdog.yml (Located in src/main/resources)

watchdog:
  token: fghaepoghaporghaerg
like image 357
Morph Avatar asked May 03 '17 08:05

Morph


2 Answers

First of all your Test class should be annotated with @Component in order for it to be registered as a bean by spring (also make sure all your classes are under your main package - the main package is where a class that is annotated with @SpringBootApplication reside).

Now you should either move all your properties to application.yml (src/main/resources/application.yml), that is picked automatically by spring boot (note that it should be .yml instead of .yaml or register a custom PropertySourcesPlaceholderConfigurer.

Example for PropertySourcesPlaceholderConfigurer:

@Bean
public static PropertySourcesPlaceholderConfigurer PropertySourcesPlaceholderConfigurer() throws IOException {
    PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
    MutablePropertySources propertySources = new MutablePropertySources();
    Resource resource = new DefaultResourceLoader().getResource("classpath:application.yml");
    YamlPropertySourceLoader sourceLoader = new YamlPropertySourceLoader();
    PropertySource<?> yamlProperties = sourceLoader.load("yamlProperties", resource, null);
    propertySources.addFirst(yamlProperties);
    configurer.setPropertySources(propertySources);
    return configurer;
}

Now your properties should be loaded to spring's environment and they will be available for injection with @Value to your beans.

like image 123
Tom Avatar answered Oct 24 '22 10:10

Tom


You basically got three easy options.

  1. Use application.properties which is Springs internal configuration file.
  2. Load your own configuration file using --spring.config.name as VM parameter.
  3. You can use @PropertySource to load either an internal or external configuration. @PropertySource only works with .properties config files. There is currently an open Jira ticket to implement yaml support. You can follow the progress here: https://jira.spring.io/browse/SPR-13912

Notice, if you are using multiple yaml and/or properties files which contain common keys, the it will always use the definition of the key which was loaded last. This is why the below example uses two different keys. If it used the same key, then it would print out PROPERTIES FILE twice.

Short simple code snippet:

@Component
@PropertySource("file:/path/to/config/app.properties")
class Address{

    @Value("${addr.street}")
    private String street;

    @Value("${addr.city}")
    private String city;
}

app.properties

addr.street=Abbey Road
addr.city=London

Extensive Example

DemoApplication.java

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(DemoApplication.class, args);

        //Call class with properties
        context.getBean(WatchdogProperties.class).test();
        //Call class with yaml
        context.getBean(WatchdogYaml.class).test();
    }

    //Define configuration file for yaml
    @Bean
    public static PropertySourcesPlaceholderConfigurer properties() {
      PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
      YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
      yaml.setResources(new ClassPathResource("watchdog.yml"));
      propertySourcesPlaceholderConfigurer.setProperties(yaml.getObject());
      return propertySourcesPlaceholderConfigurer;
    }
}

WatchdogProperties.java

@Component
//PropertySource only works for .properties files
@PropertySource("classpath:watchdog.properties")
public class WatchdogProperties{
    //Notice the key name is not the same as the yaml key
    @Value("${watchdog.prop.token}")
    private String token;

    public void test(){
        System.out.println(token);
    }
}

WatchdogYaml.java

@Component
class WatchdogYaml{
    //Notice the key name is not the same as the properties key
    @Value("${watchdog.token}")
    private String token;

    public void test(){
        System.out.println(token);
    }
}

Properties and Yaml files Both of these files are located in src/main/resources

watchdog.yml:

watchdog:
  token: YAML FILE

watchdog.properties:

watchdog.prop.token=PROPERTIES FILE

Output

PROPERTIES FILE
YAML FILE
like image 42
kkflf Avatar answered Oct 24 '22 09:10

kkflf