Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating beans on-demand using spring

Tags:

I want to create some spring beans after startup in a factory-ish pattern. For example every so often I have some work to do and I need to create a task bean (which probably has dependents on other singleton spring beans) and execute it.

There may be several pieces of work to execute concurrently so each task bean needs to be independent (prototype).

Is there any common pattern people use to achieve this?

As I see it I need to interact with the container/applicationContext somehow but I don't really want to scatter injections of applicationContext/beanFactory and calls to getBean("...") everywhere.

I thought of something like this (note the "factory" is something I'm imagining, rather than something that exists)

<bean id="myTask" class="MyTask" scope="prototype">
  <property name="entityManager" ref=".../>
  ...
</bean>

<bean id="myTaskExecutor" class="MyTaskExecutor">
  <property name="taskFactory">
     <xxx:factory bean="myTask"/>
  </property>
</bean>

And then code

class MyTaskExecutor
{
  private Factory<MyTask> taskFactory;

  public void setTaskFactory( Factory<MyTask> taskFactory )
  {
    this.taskFactory = taskFactory;
  }
}

And maybe an annotation version

class MyTaskExecutor
{
  @Factory(MyTask.class)
  private Factory<MyTask> taskFactory;

}

Maybe there's something like the above already? Or am I missing something fundamental somewhere.

I realise I could have a singleton MyTaskFactory and use that to instantiate using "new" but then I'd have to pass all of it's dependents from the factory which feels wrong.

So I guess to sum up the question is

What is the recommended way of creating prototype spring beans on-demand from within application code?

Appreciate any input.

like image 382
Mike Q Avatar asked Jan 24 '10 19:01

Mike Q


1 Answers

I think you're over-complicating the problem. All you need to do is write a TaskFactory class (nothing special about it, no special interfaces or annotations). TaskFactory would be injected with all the other beans needed, and would have a createTask method which creates the tasks on demand, and which passes references to the required Spring beans to the new task when it's created. Client code is injected with TaskFactory, and calls createTask when required.

Spring itself provides no explicit support for what you're trying to do. The likes of factory-method XML attributes and FactoryBean interfaces are only useful for one-off creation of a bean within its scope, and if you want to create them on demand, that means scope="prototype", and that means using getBean().

edit: It's probably worth pointing out that prototype-scoped beans are really not what Spring is designed for. Yes, it supports them, but using them is not a very edifying experience. If you really want to go down this road, then it's worth taking a look at @Configurable. It's very powerful, but not always suitable, because of runtime classloader constraints.

like image 90
skaffman Avatar answered Sep 30 '22 13:09

skaffman