Every time we request a bean, Spring shall create and inject a new instance of bean as the dependency. This is different from Singleton Bean where only one instance is created application context wide and reused again and again. Most Beans in a typical Spring Application are declared Singleton.
Spring DI - Singleton beans with prototype-bean dependencies
Upasana | April 27, 2019 | 3 min read | 1,544 views
Injecting Prototype Bean into Singleton Bean
In most application scenarios, most beans in the container are singletons. When a singleton bean needs to collaborate with another singleton bean, or a non-singleton bean needs to collaborate with another non-singleton bean, you typically handle the dependency by defining one bean as a property of the other.
When you use singleton-scoped beans with dependencies on prototype beans, be aware that dependencies are resolved at instantiation time. Thus if you dependency-inject a prototype-scoped bean into a singleton-scoped bean, a new prototype bean is instantiated and then dependency-injected into the singleton bean. The prototype instance is the sole instance that is ever supplied to the singleton-scoped bean.
What is challenge?
So problem arises when the bean lifecycles are different. Suppose singleton bean A needs to use non-singleton (prototype) bean B, perhaps on each method invocation on A. The container only creates the singleton bean A once, and thus only gets one opportunity to set the properties. The container cannot provide bean A with a new instance of bean B every time one is needed on method invocation.
Spring Boot - Lookup method injection
Lookup method injection shall be used in Spring Boot Applications to Inject a Prototype Bean into Singleton Bean.
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.concurrent.ThreadLocalRandom;
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeBean {
private int dateTimeString = ThreadLocalRandom.current().nextInt(); (1)
public int getSeed() {
return dateTimeString;
}
}
1 | We are injecting a Random Seed value at Prototype Bean Construction Time, so if getSeed() returns same value, its the same instance of Bean otherwise a new instance will show a different value for each method call. |
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.stereotype.Component;
@Component
public class SingletonBean {
public void showMessage() {
PrototypeBean bean = getPrototypeBean(); (1)
System.out.println("The prototype bean version is " + bean.getSeed());
}
@Lookup (2)
public PrototypeBean getPrototypeBean() {
//Do not provide method implementation, spring will override this method behind the scenes.
return null;
}
}
1 | Everytime this method is called, a new instance of Prototype Bean should be created, so we shall see a different seed value on each method call. |
2 | Spring DI will lookup the actual bean at runtime, so we do not need to provide an implementation here. |
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public CommandLineRunner commandLineRunner(SingletonBean singletonBean, ApplicationContext ctx) {
return args -> {
singletonBean.showMessage(); (1)
singletonBean.showMessage();
singletonBean.showMessage();
SpringApplication.exit(ctx, () -> 0);
};
}
}
1 | Each method invocation shall create a new instance of Prototype Bean, so we shall see a different seed value each time. |
2017-12-27 10:11:48.982 INFO 44948 --- [ main] .s.b.c.e.j.JettyEmbeddedServletContainer : Jetty started on port(s) 8080 (http/1.1)
The prototype bean version is 183657962
The prototype bean version is 1467467319
The prototype bean version is -319235431
How to do it wrongly?
If you directly inject a Protoype Bean into Singleton Bean, without a @Lookup method, then a single instance of Protoype Bean will be created and that’s wrong approach.
Top articles in this category:
- What are different Bean Scopes in Spring?
- Unresolved circular dependency in spring dependency injection
- SendGrid Attachments with Spring Boot
- Dialoglfow fulfillment with Spring Boot
- Elasticsearch with Spring Boot + Spring Data
- Sendgrid Dynamic Templates with Spring Boot
- Spring Data ElasticSearch with Basic Auth