Spring-Boot Configuration Properties

I wanted to switch in my spring-boot microservices project from hard coded values to configurable ones:

// ...
JsonNode json = restTemplate.postForObject("http://localhost:8081/login",
  payload, ObjectNode.class);
// ...

The easiest way to configure anything in spring-boot is through application.properties. By default, spring-boot looks for the application.properties in the /config folder first, so I placed one there with the desired value in it:

~/microservices/webservice % cat src/main/resources/config/application.properties 

services.authservice.url=http://localhost:8081/

~/microservices/webservice %

There are two ways to access configuration values in spring-boot: using the @Value annontation or with structured objects. I’m going to use the structured objects, because the @Value annotation can be cubersome to use when it comes to types (and it is also a bit hard to mock it in testing):

package com.zsoltfabok.webservice.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix="services")
public class ServicesProperties {

    private Authservice authservice = new Authservice();

    public static class Authservice {
        private String url;

        public String getUrl() { return url; }
        public void setUrl(String url) { this.url = url; }
    }

    public Authservice getAuthservice() { return authservice; }
    public void setAuthservice(Authservice authservice)
      { this.authservice = authservice; }
}

The ServiceProperties is bounded to all the variables that start with the word services. The authservice is a sub level property and as such spring-boot reaches it with a static sub class. Mind that the getters and setters must much the configuration name. For example, in my case the getAuthService() and setAuthService() methods did nothing, and when I asked for the value, the system returned null.

In order to use the class above I had to do two things. First, tell spring-boot to load it with the @EnableConfigurationProperties annotation:

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

Second, inject it into my contoller (that’s why the properties file is annotated with @Component):

@RequestMapping("/login")
@Controller
public class LoginController {

    @Autowired
    private ServicesProperties servicesProperties;

    // ...
 }

Using the property:

// ...
String authserviceUrl = servicesProperties.getAuthservice().getUrl();
JsonNode json = restTemplate.postForObject(authserviceUrl,
  payload, ObjectNode.class);
// ...

Since it is autowired, mocking it in testing is also easy:

@RunWith(SpringRunner.class)
@WebMvcTest(LoginController.class)
public class LoginControllerTest {
	// ...

    @MockBean(answer=Answers.RETURNS_DEEP_STUBS)
    private ServicesProperties servicesProperties;

    @Before
    public void setup() {
        authserviceUrl = "url";
        when(servicesProperties.getAuthService().getUrl())
          .thenReturn(authserviceUrl);
    }

    // ...
}

The answer=Answers.RETURNS_DEEP_STUBS tells mockito to allow to mock chain of methods. Otherwise, I had to create a mock for ServicesProperties and another one for the ServicesProperties.Authservice.


comments powered by Disqus