Subscribe for updates on posts
Be the first to read the latest news

Dynamically create Spring beans from groovy scripts at runtime

February 24th, 2009 by Marius Hanganu in Java, General

Groovy

In the current project – a java based CMS – we were faced with an interesting problem: how can we dynamically create renderers to power our blocks in the html page (currently all renderers are Java classes and Spring beans also described in the application context files).

Imagine the power of this approach. Especially when Java deployments can be quite a hassle in a multi server environment (deploying sources on the linux machines, wait for a good moment for starting/stopping the servers – which may or may not perform a sensitive operation at that time, synchronize the restart of the web servers, check log files for any errors, etc).

What could be better than having your Spring beans dinamically created from the administration panel? No server restarts, no deployments. Just keep your Groovy scripts in the database and make any modification realtime.

Achieving the programatic creation of Spring beans from groovy scripts can be done in four steps:

1. load the groovy script and parse it into a java Class
2. create a bean definition using the previously loaded class
3. inject any other beans into your bean definition
4. register the bean definition and get a handle on the bean created

Of course, you cannot declare variables as instances of the Java class defined in the groovy script. Or you may do it programatically through reflection. But it is much simpler (if the project suits these needs) to have a base interface for all your dynamic beans and after those 4 steps, simply cast the bean created to the Java interface. So you might add two more steps to the previous four:

0. declare a base interface for your all your classes declared in groovy scripts
5. cast the created bean to the base interface so that you can use it in your code

The code is actually pretty simple. But you can hardly find any article/post on the subject. Most of them discuss an older version of Spring (1.x), while my code had to work with Spring 2.0.x – which is completely different in terms of the API calls needed to create a bean.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class GroovyTemplateEngineImpl extends DefaultListableBeanFactory implements ApplicationContextAware {
    public BaseInterface createOrUpdateBean(String rendererName, String code) throws Exception, ClassNotFoundException {
 
        // 1. load the groovy script and parse it into a class
        GroovyClassLoader gcl = new GroovyClassLoader(getClassLoader());
        Class clazz = gcl.parseClass(groovyScriptAsAString);
 
        // 2. create the bean definition
        AbstractBeanDefinition beanDef = BeanDefinitionReaderUtils.createBeanDefinition( "parentBean", packageName + "." + className, gcl);
 
        // 3. inject here any attributes that would normally be passed using spring XML configuration files
        beanDef.setAttribute("attr1", bean1);
        DefaultListableBeanFactory factory = (DefaultListableBeanFactory) appCtx.getAutowireCapableBeanFactory();
 
        // 4. Create the bean - I'm using the class name as the bean name
        factory.registerBeanDefinition(className, beanDef);
        Object bean = factory.createBean(clazz, AUTOWIRE_BY_NAME, false);
 
        // 5. further on you can cast it to any interface
        return (BaseInterface) bean;
    }
}

You will also need to add to the previous class a setter for application context to be injected (the class needs to implement ApplicationContextAware).

Please note that this code only works from spring version 2.0.3 and up. If you’re using a lower version, you’ll likely encounter this exception:

1
2
3
Caused by: org.aspectj.weaver.reflect.ReflectionWorld$ReflectionWorldException: 
warning can't determine implemented interfaces of missing type .... 
[Xlint:cantFindType]

Good luck groovying!


You might also like

Avoid Spring circular references and over eager type matching using lazy initialization Circular dependencies between beans managed by Spring is usually caused by a logic error, but it may...
Springy: org aspectj weaver reflect ReflectionWorld ReflectionWorldException: warning can’t determine implemented interfaces of missing type Here's a quick one: if you've encountered the following error org.springframework.beans.factory.BeanCreationException:...
How to order by a custom SQL formula/expression when using hibernate Criteria API In our current project we are using Spring + Hibernate Annotations. Today I needed to use something...
Spring+JPA with MySQL/Oracle configurations During development for a solution based on Spring+JPA+MySQL/Oracle we came across two settings to...
.

One Response

  1. Marius Hanganu Says:

    I could not get the following to work:

    beanDef.setAttribute(“attr1”, bean1);

    The following alternative worked for me:

    MutablePropertyValues v = new MutablePropertyValues();
    v.addPropertyValue(“attr1”,bean1);

Leave a Comment

Please note: Comment moderation is enabled and may delay your comment. There is no need to resubmit your comment.