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