Spring has the nice mechanism PropertyPlaceholderConfigurer for injecting values like timeouts, JDBC Urls and so forth into Spring beans for configuration purposes. Is there a sensible way to handle configuration values that can change at runtime?
UPDATE: With Spring 3.1 there is a nice way to include non-static configuration sources such as the database via PropertySources. Some ApplicationContexts provide a refresh mechanism that is in principle able to handle changing configuration values. However it stops the application first, then creates all beans fresh and then starts the application context again. However, for our purposes I would need a way to do this transparently, such that the server correctly handles currently running requests.
Another idea to do this would be a custom Scope that creates fresh objects when the configuration changes. Unfortunately the ObjectFactory provided to the Scope uses a preprocessed bean definition, such that the placeholders are not read anew from the configuration. Thus the created objects have the same configuration. :-(
Unfortunately configuration from properties
files is static and happens at startup. What I am typically doing is exposing dynamic attributes via jmx:
@ManagedResource
@Service
public class BusinessService {
@ManagedAttribute
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void businessMethod() {
//use age...
}
}
Remember to add:
<context:mbean-export/>
To your configuration. Now you can access and change that attribute via jconsole
or any other JMX client. See also: 23.3.2 Using Source-Level Metadata (JDK 5.0 annotations).
The following is a little odd but works. You create a custom scope named reconfigurable
that throws away all beans created in that scope whenever a configuration update occurs. Thus, a fresh bean will be created after a configuration change.
The actual configuration values have to be retrieved via spring expression language since the values for both the normal ${} syntax nor PropertyOverrideConfigurer seem to be permanently fixed in the BeanDefinition. A bean declaration for a bean with a re-configurable property someProperty
looks like this:
<bean class="blablu.Testbean" scope="reconfigurable"
p:someProperty="#{ config['configexplicit']}">
<aop:scoped-proxy />
</bean>
You need to use aop:scoped-proxy such that beans that use this bean always retrieve the freshest configured bean from the custom scope.
Declaring properties with @Value
also works; if you use component scan you need to declare the scope with the annotation
@Scope(value="reconfigurableScope", proxyMode=ScopedProxyMode.TARGET_CLASS)
If you care for details: the basic idea of the scope is:
public class ReconfigurableScope implements Scope {
private final Map<String, Object> nameToObjectMap = new ConcurrentHashMap<String, Object>();
public Object get(final String name, final ObjectFactory<?> objectFactory) {
Object bean = nameToObjectMap.get(name);
if (null == bean) {
bean = objectFactory.getObject();
nameToObjectMap.put(name, bean);
}
return bean;
}
// called from outside on each configuration change
public void update(final ConfigurationObservable observable, final Object arg) {
nameToObjectMap.clear();
}
}
Plus some thread safety and cleanup stuff: the removed beans need to be destroyed a little later and on application context close.
There is a running example of what you're trying to accomplish here: https://github.com/ldojo/spring-cloud-config-examples
It demonstrates how a Spring Cloud Config Server and a client service can communicate over Spring Cloud Bus, and the client's configuration properties can change at runtime when there is a configuration change in the Config Server's repo.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With