Tuesday, March 24, 2009

Create Spring beans dynamically

Spring XML declared beans can be wired building your business structure. The problem with this approach is that these beans are loaded on the start up, when the Spring Context is created, which does not leave much of playground to modify or change beans at run time. How ever Spring offers opportunity to load beans in the Spring context on run time when beans are needed, and populate them at need.
For doing this, we need the Spring Application context. For this to accomplish, one can declare one dedicated Spring context bean, which will implement the ApplicationContextAware and BeanFactoryPostProcessor interfaces. This should look something like this:


public class MyContextWrapper implements ApplicationContextAware,
BeanFactoryPostProcessor {

private ApplicationContext appContext;
private ConfigurableListableBeanFactory factory;

public void postProcessBeanFactory(ConfigurableListableBeanFactory factory)
throws BeansException {
this.factory = factory;
}
public void setApplicationContext(ApplicationContext c)
throws BeansException {
this.appContext = c;
}

//setters and getters

}



The postProcessBeanFactory will be executed after reading the configuration files of the spring context. Here one can do other configurations and bean loading, or save the factory for later use.
The Spring application context can be used for later dynamic bean retrieval or other purposes.

For this approach to work, one should let spring load this bean in to it's context by declaring the bean in the XML configuration file as follow:






Now this bean can be loaded in any other bean of the application via referencing it:








Now that we have the grip of the Spring context and it's factory, we can create beans dynamically. For this purpose, we can use the GenericBeanDefinition which allows to load bean definitions as follows:

BeanDefinitionRegistry registry = ((BeanDefinitionRegistry )factory);

GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(MyBeanClass.class);
beanDefinition.setLazyInit(false);
beanDefinition.setAbstract(false);
beanDefinition.setAutowireCandidate(true);
beanDefinition.setScope("session");

registry.registerBeanDefinition("dynamicBean",beanDefinition);


As one can see, this bean is created in the session scope and will be stored in the session of the user. The property auto wire candidate tells spring if dependency's of the bean such as setter's or getter's or constructor argument's should be handled automatically by Spring. The property lay init tells Spring if this bean should be instantiated when needed.

To get a handle of Spring bean, one can use the Spring application context as follows:

Object bean=
getApplicationContext().getBean("dynamicBean");
if(bean instanceof MyBeanClass){
MyBeanClass myBean = (MyBeanClass) bean;

// do with the bean what ever you have to do.
}


As you can see, the dynamic bean creation in Spring is no big deal.It is easy to use, an certainly handy in many situations.

6 comments:

  1. this is a cool walkthrough. cheers.

    ReplyDelete
  2. I get a type cast error when i do this :





    Here my factory bean is a SchedulerFactoryBean.

    Can you help ?

    Thanks,
    Kris

    ReplyDelete
  3. Sorry Kris, I can't see your code. Is this type cast error when you cast on ShedulerFactoryBean or is it thrown when this factroy is giving you a handle on a bean. If the later is the case, I would recommend to see if the name you use is the correct name of your bean and if you cast it to the right type.

    Hope it works for you.


    @luca ghirotti
    thanx

    ReplyDelete
  4. hi agema, thanks for the information. I am new to spring.
    I am extending an existing application which uses spring and properties like datasource, user credentials are added in the context before initialise. however this will not suit for my enhancement, I am looking to change some properties like datasource name, user credentials during runtime. Could you please share your thoughts, whether can I use Beandefinition and set the properties,
    I have added something like this, by my problem is, when i register the bean definition, will the parent applicationcontext holds this new bean value?
    BeanDefinition definition = new RootBeanDefinition(PseudoDataSource.class);
    ApplicationContext context = new ClassPathXmlApplicationContext("WEB-INF/applicationContext.xml");
    definition.getPropertyValues().addPropertyValue("driverName", "");
    definition.getPropertyValues().addPropertyValue("dataSourceName", "");
    definition.getPropertyValues().addPropertyValue("userName", "");
    definition.getPropertyValues().addPropertyValue("password", "");
    Object bean = context.getBean("myBeanFactory");
    BeanDefinitionRegistry registry = ((BeanDefinitionRegistry )(DataSourceBeanPostProcessor)bean);
    registry.registerBeanDefinition("schemaDataSource", definition);

    can you please help?

    ReplyDelete
  5. Hi valluri, I haven't done such thing before. But why trying to change the configuration. Let Spring create you a new Instance of the Bean and change the properties on the fresh instance. I don't know if that will work, but it is a worth of trying.

    Hope it helps somehow.

    kind regards.

    ReplyDelete
  6. Thank you very much for the information it is very helpful.

    regards
    rajesh

    ReplyDelete