Reputation: 918
I am using Spring Boot, and loading/generating application properties in @Configuration bean on startup of my app, and placing them in HashMap, for example:
@Configuration
public class Config {
@Bean(name = "appConfig")
public Map<String, String> getApplicationConfig() {
Map<String, String> appConfig = new HashMap<>();
//load or generate global config varibles
appConfig.put("GLOBAL_CONFIG", "VALUE 1");
return appConfig;
}
I am injecting this map of properties in several beans, for example:
@RestController
@RequestMapping("/getglobalconfig")
public class ConfigController {
@Value("#{appConfig}")
private Map<String, String> appConfig;
@GetMapping
public String getGlobalConfig() { return appConfig.get("GLOBAL_CONFIG"); }
}
After some event I want to change for example the value of my appConfig HashMap GLOBAL_CONFIG value in a thread safe way, so that it's updated in all injected beans, like for example in the ConfigController example above.
What would be the best pattern to accomplish this in a thread safe way and have my beans reloaded value without re-initializing them or restarting the app. Spring cloud config looks interesting, but it's unfortunately unavailable for me.
I am not limited to HashMap properties, I just need a thread safe reloading of global property so that it's visible in injected beans after reload.
Upvotes: 1
Views: 2091
Reputation: 2220
Spring creates singletons by default so all beans will have the same reference to the properties map. However, concurrent access to simple map may produce error.
So minimal required changes are
Map<String, String> appConfig = new java.util.concurrent.ConcurrentHashMap<>();
//load or generate global config varibles
appConfig.put("GLOBAL_CONFIG", "VALUE 1");
return appConfig;
or
Map<String, String> appConfig = new HashMap<>();
//load or generate global config varibles
appConfig.put("GLOBAL_CONFIG", "VALUE 1");
return java.util.Collections.synchronizedMap(appConfig);
However, approach you use means other beans could change properties too. I would create new wrapper bean around map which provides only one method
public class ReadOnlyConfig {
private final Map map;
public ReadOnlyConfig (Map map) { this.map = map; }
String getProperty(String name) { return map.get(name); }
}
and
@Configuration
public class Config {
@Bean(name = "appConfig")
public Map<String, String> getApplicationConfig() {
Map<String, String> appConfig = new java.util.concurrent.ConcurrentHashMap<>();
//load or generate global config varibles
appConfig.put("GLOBAL_CONFIG", "VALUE 1");
return appConfig;
}
@Bean(name = "readOnlyConfig")
public ReadOnlyConfig getReadOnlyApplicationConfig( @Qualifier(name = "appConfig") Map<String, String> map) {
return new ReadOnlyConfig(map);
}
Upvotes: 1