Creating a dynamically initialized bean in Spring

David Barda
davebarda
Published in
4 min readAug 26, 2018

--

If you are using Spring for a while, you probably got into a situation where you need to initialize a Spring bean that receives different arguments as an input in each of its inialization. In the first time that I have encountered that issue, I’ve assumed it will be simple and conventional, as Spring has its Prototype scope, which is creating us a new instance in each time that a request to that bean is made, there are two interesting issues regarding this matter:

  1. How to instantiate the Prototype from a Singleton bean(which is being used in most of the situations).
  2. How to transfer different dynamic parameter to the Prototype bean.

In order to solve these issues, I asked google for help, but all that I’ve found was a lot of information and methods that are scattered over blogs and StackOverflow threads, so I’ve decided to summarize the knowledge into a single blog post. In that post I’ll examine the different methods that spring supplies, so you could examine the situation and choose the one which fit the best.

Creating a new bean using the Application context

This method is the most naive one, using ApplicationContext.getBean(String beanName, args…) method to pass the arguments.

Here is an example:

@Configuration
public class MyConf {
@Bean(name = "MyPrototype")
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public MyPrototype createPrototype(String arg) {
return new MyPrototype(arg);
}
}
public class MyPrototype {
private String arg;
public MyPrototype(String arg) {
this.arg = arg;
}
public void action() {
System.out.println(arg);
}
}
@Component
public class UsingMyPrototype {
private ApplicationContext applicationContext;
@Autowired
public UsingMyPrototype(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public void usePrototype() {
final MyPrototype myPrototype = (MyPrototype)
applicationContext.getBean("MyPrototype", "hello");
myPrototype.action();
}
}

There are many problems with this method:

  1. In IoC terms, we are giving all the control to the context, and many beans will directly depend on the ApplicationContext.
  2. ApplicationContext as a dependency doesn’t give us much information.
  3. It can make it harder to create Integration tests(We can’t just replace the implementation of the bean we are depended on).

Lookup method

Lookup method injection, a way of defining a method that will create a new instance of the bean lazily(Similar to what happened when you inject bean scope with proxyMode = ScopedProxyMode.TARGET_CLASS)

Here is a simple example for lookup method:

@Configuration
public class MyConf {
@Bean(name = "MyPrototype")
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public MyPrototype createPrototype(String arg) {
return new MyPrototype(arg);
}
}
public class MyPrototype {
private String arg;
public MyPrototype(String arg) {
this.arg = arg;
}
public void action() {
System.out.println(arg);
}
}
@Component
public class UsingMyPrototype {
@Lookup
public MyPrototype getPrototype(String arg) {
return null;
}
public void usePrototype() {
MyPrototype myPrototype = getPrototype("hello");
myPrototype.action();
}
}

There are few drawbacks in this method too:

  1. As this method is proxying the calls using CGLIB, neither the class or the method can’t be final.
  2. It’s an inexplicit method, which can cause confusion to someone who read it and never encountered Lookup methods before.

Factory method

This method enables you to autowire a single bean, and to pass a different argument to the prototype whenever we want to, just as simple as we want it to be.

@Configuration    
public class ServiceConfig {
@Bean
public Function<String, MyPrototype> myPrototypeFactory() {
return arg -> myPrototype(arg);
}
@Bean
@Scope(value = "prototype")
public MyPrototype myPrototype(String arg) {
return new MyPrototype(arg);
}
}
@Component
public class UsingMyPrototype {
@Autowired
private Function<String, MyPrototype> myPrototypeFactory;
public void usePrototype(String arg) {
myPrototypeFactory.apply(arg);
}
}

ObjectProvider(Since Spring 4.3)

Since Spring 4.3, there is a new way which was sewed for that issue, ObjectProvider — It enables you just to add it as a dependency to your “argumented” Prototype scoped bean and to instantiate it using the argument.

So here is an example that shows how to work with the ObjectProvider:

@Configuration
public class MyConf {
@Bean
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public MyPrototype createPrototype(String arg) {
return new MyPrototype(arg);
}
}
public class MyPrototype {
private String arg;
public MyPrototype(String arg) {
this.arg = arg;
}
public void action() {
System.out.println(arg);
}
}
@Component
public class UsingMyPrototype {
private ObjectProvider<MyPrototype> myPrototypeProvider;
@Autowired
public UsingMyPrototype(ObjectProvider<MyPrototype>
myPrototypeProvider) {
this.myPrototypeProvider = myPrototypeProvider;
}
public void usePrototype() {
final MyPrototype myPrototype =
myPrototypeProvider.getObject("hello");
myPrototype.action();
}
}

Its drawbacks are:

  1. The getObject method isn’t type safe.
  2. It’s creating coupling to the Spring framework(which we would like to minimize)

It has also a benefit over the bean factory, which is the methods that ObjectProvider supplies and its explicitness.IMO those two last methods usually fit the best, and it doesn’t seem that they are popular enough, consider to use them in your projects.

I hope you enjoyed the reading and that it made the process of parameterizing Spring beans easier for you.
Please leave comments with your thoughts, questions, and enlightenments.

--

--