Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I replace a Spring bean definition at runtime?

Tags:

java

spring

Consider the following scenario. I have a Spring application context with a bean whose properties should be configurable, think DataSource or MailSender. The mutable application configuration is managed by a separate bean, let's call it configuration.

An administrator can now change the configuration values, like email address or database URL, and I would like to re-initialize the configured bean at runtime.

Assume that I can't just simply modify the property of the configurable bean above (e.g. created by FactoryBean or constructor injection) but have to recreate the bean itself.

Any thoughts on how to achieve this? I'd be glad to receive advice on how to organize the whole configuration thing as well. Nothing is fixed. :-)

EDIT

To clarify things a bit: I am not asking how to update the configuration or how to inject static configuration values. I'll try an example:

<beans>     <util:map id="configuration">         <!-- initial configuration -->     </util:map>      <bean id="constructorInjectedBean" class="Foo">         <constructor-arg value="#{configuration['foobar']}" />     </bean>      <bean id="configurationService" class="ConfigurationService">         <property name="configuration" ref="configuration" />     </bean> </beans> 

So there's a bean constructorInjectedBean that uses constructor injection. Imagine the construction of the bean is very expensive so using a prototype scope or a factory proxy is not an option, think DataSource.

What I want to do is that every time the configuration is being updated (via configurationService the bean constructorInjectedBean is being recreated and re-injected into the application context and dependent beans.

We can safely assume that constructorInjectedBean is using an interface so proxy magic is indeed an option.

I hope to have made the question a little bit clearer.

like image 451
Philipp Jardas Avatar asked Oct 28 '10 09:10

Philipp Jardas


People also ask

Does Spring allow more than one bean definition in the configuration?

Spring Couldn't autowired,there is more than one bean of `` type. Bookmark this question.

Can we use @component instead of @bean?

@Component is a class-level annotation, but @Bean is at the method level, so @Component is only an option when a class's source code is editable. @Bean can always be used, but it's more verbose. @Component is compatible with Spring's auto-detection, but @Bean requires manual class instantiation.

Can we override bean in Spring?

Spring beans are identified by their names within an ApplicationContext. Thus, bean overriding is a default behavior that happens when we define a bean within an ApplicationContext which has the same name as another bean. It works by simply replacing the former bean in case of a name conflict.


2 Answers

Here is how I have done it in the past: running services which depend on configuration which can be changed on the fly implement a lifecycle interface: IRefreshable:

public interface IRefreshable {   // Refresh the service having it apply its new values.   public void refresh(String filter);    // The service must decide if it wants a cache refresh based on the refresh message filter.   public boolean requiresRefresh(String filter); } 

Controllers (or services) which can modify a piece of configuration broadcast to a JMS topic that the configuration has changed (supplying the name of the configuration object). A message driven bean then invokes the IRefreshable interface contract on all beans which implement IRefreshable.

The nice thing with spring is that you can automatically detect any service in your application context that needs to be refreshed, removing the need to explicitly configure them:

public class MyCacheSynchService implements InitializingBean, ApplicationContextAware {  public void afterPropertiesSet() throws Exception {   Map<String, ?> refreshableServices = m_appCtx.getBeansOfType(IRefreshable.class);   for (Map.Entry<String, ?> entry : refreshableServices.entrySet() ) {    Object beanRef = entry.getValue();    if (beanRef instanceof IRefreshable) {     m_refreshableServices.add((IRefreshable)beanRef);    }   }  } } 

This approach works particularly well in a clustered application where one of many app servers might change the configuration, which all then need to be aware of. If you want to use JMX as the mechanism for triggering the changes, your JMX bean can then broadcast to the JMS topic when any of its attributes are changed.

like image 120
Justin Avatar answered Sep 20 '22 00:09

Justin


I can think of a 'holder bean' approach (essentially a decorator), where the holder bean delegates to holdee, and it's the holder bean which is injected as a dependency into other beans. Nobody else has a reference to holdee but the holder. Now, when the holder bean's config is changed, it recreates the holdee with this new config and starts delegating to it.

like image 37
shrini1000 Avatar answered Sep 24 '22 00:09

shrini1000