Skip to content

Instantly share code, notes, and snippets.

@ctranxuan
Last active November 21, 2017 23:32
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ctranxuan/d5c341579dcf8f2f62e129fcfdc4836a to your computer and use it in GitHub Desktop.
Save ctranxuan/d5c341579dcf8f2f62e129fcfdc4836a to your computer and use it in GitHub Desktop.
SpringBoot things

How to load @ConfigurationProperties manually from a Yaml configuration file

Let's say we have the following yaml configuration file:

foo:
    apis:
      -
        name: Happy Api
        path: /happyApi.json?v=bar
      -
        name: Grumpy Api
        path: /grumpyApi.json?v=grrr

and we have the following ConfigurationProperties:

@ConfigurationProperties(prefix = "foo")
public class ApisProperties {
    private List<ApiPath> apis = Lists.newArrayList();

    public ApisProperties() {
    }

    public List<ApiPath> getApis() {
        return apis;
    }

    public static class ApiPath {
        private String name;
        private String path;

        public String getName() {
            return name;
        }

        public void setName(final String aName) {
            name = aName;
        }

        public String getPath() {
            return path;
        }

        public void setPath(final String aPath) {
            path = aPath;
        }
    }
}

Then, to do the "magic" things of Spring Boot programmatically (e.g. loading some properties in a static method), you can do:

   private static ApisProperties apiProperties() {
        try {
            ClassPathResource resource;
            resource = new ClassPathResource("/config/application.yml");

            YamlPropertiesFactoryBean factoryBean;
            factoryBean = new YamlPropertiesFactoryBean();
            factoryBean.setSingleton(true); // optional depends on your use-case
            factoryBean.setResources(resource);

            Properties properties;
            properties = factoryBean.getObject();

            MutablePropertySources propertySources;
            propertySources = new MutablePropertySources();
            propertySources.addLast(new PropertiesPropertySource("apis", properties));
            
            ApisProperties apisProperties;
            apisProperties = new ApisProperties();

            PropertiesConfigurationFactory<ApisProperties> configurationFactory;
            configurationFactory = new PropertiesConfigurationFactory<>(apisProperties);
            configurationFactory.setPropertySources(propertySources);
            configurationFactory.setTargetName("foo"); // it's the same prefix as the one defined in the @ConfigurationProperties

            configurationFactory.bindPropertiesToTarget();
            return apisProperties; // apiProperties are fed with the values defined in the application.yaml

        } catch (BindException e) {
            throw new IllegalArgumentException(e);

        }
    }

How to inject a String comma separated values as a list?

@Value("#{'${request.headersToFilter}'.trim().replaceAll(\"\\s*(?=,)|(?<=,)\\s*\", \"\").split(',')}")
private Set<String>  myList;

Works with an array of String String[] myList too.

Preferred practices

## Configuration

Inject the values as parameters and not @Autowired field: in this case, you can still create the objects without Spring, just by instanciating the Configuration and calling a factory method with the right argument values:

@Configuration
public class FooConfiguration {

    @Bean
    public Bar barParser(
            @Value("#{'${bar.fieldsToFilter}'.trim().replaceAll(\"\\s*(?=,)|(?<=,)\\s*\", \"\").split(',')}")
            String[] aFieldsToFilter) {
        return new Bar(aFieldsToFilter);
    }
}

... somewhere in unit tests, for example:

new FooConfiguration().barParser(new String[] { "hello", "world" });

Unit and integration tests...

How to load yaml configuration files and inject @Value?

@ContextConfiguration(classes = {
        MyConfiguration.class,
        PropertySourcesPlaceholderConfigurer.class },
        initializers = ConfigFileApplicationContextInitializer.class)
@RunWith(SpringRunner.class)
public class MyWonderfulClassShould {

   @Test
   public void sayHelloWorld() {
     // ...
   }
}

All the trick is in the use of PropertySourcesPlaceholderConfigurer.class and ConfigFileApplicationContextInitializer.class (see http://stackoverflow.com/questions/33815563/using-configfileapplicationcontextinitializer-does-not-resolve-default-values-fo/38424468#38424468).

How to load only a set of configuration without the whole application?

@ContextConfiguration(classes = {
        FooConfiguration.class,
        BarConfiguration.class,
        PropertySourcesPlaceholderConfigurer.class },
        initializers = ConfigFileApplicationContextInitializer.class)
@RunWith(SpringRunner.class)
public class MyWonderfulClassShould {

   @Test
   public void sayHelloWorld() {
     // ...
   }
}

How to override a property configuration declared in a Spring configuration file?

@ContextConfiguration(classes = {
        MyConfiguration.class,
        PropertySourcesPlaceholderConfigurer.class },
        initializers = ConfigFileApplicationContextInitializer.class)
@TestPropertySource(properties = { "foo.bar=hello world!!!" })
@RunWith(SpringRunner.class)
public class MyWonderfulClassShould {

   @Test
   public void sayHelloWorld() {
     // ...
   }
}

All the trick is in the use of @TestPropertySource.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment