In Spring the scope of a bean defines the lifecycle and visibility of that bean in the Spring container. There are six scopes:
The last four scopes mentioned, request, session, application and websocket, are only available in a web-aware application.
In Spring, singleton beans and their dependencies are initialized when the application context is created.
If a Singleton bean depends on a bean with a shorter-lived scope (like Request or Session beans), it retains
the same instance of that bean, even when new instances are created for each Request or Session. This mismatch can cause unexpected behavior and bugs,
as the Singleton bean doesn’t interact correctly with the new instances of the shorter-lived bean.
This rule raises an issue when non-singleton beans are injected into a singleton bean.
When a Singleton bean has a dependency on a bean with a shorter-lived scope, it can lead to the following issues:
Inject a shorter-lived bean into a Singleton bean using ApplicationContext, Factories or
Providers.
When a Singleton bean auto-wires a Request bean, the dependency is resolved at instantiation time and thus the same
instance is used for each HTTP request.
@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestBean {
//...
}
public class SingletonBean {
@Autowired
private final RequestBean requestBean; // Noncompliant, the same instance of RequestBean is used for each HTTP request.
public RequestBean getRequestBean() {
return requestBean;
}
}
Instead, use a ObjectFactory<RequestBean>, ObjectProvider<RequestBean>, or
Provider<RequestBean> as injection point (as for JSR-330).
Such a dependency is resolved at runtime, allowing for actual injection of a new instance of the shorter-lived bean on each HTTP request.
@Component
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class RequestBean {
//...
}
public class SingletonBean {
private final ObjectFactory<RequestBean> requestBeanFactory;
@Autowired
public SingletonBean(ObjectFactory<RequestBean> requestBeanFactory) {
this.requestBeanFactory = requestBeanFactory;
}
public RequestBean getRequestBean() {
return requestBeanFactory.getObject();
}
}
When a Singleton bean auto-wires a Prototype bean, the dependency is resolved at instantiation time and thus the same
instance is used for each bean request.
@Component
@Scope("prototype")
public class PrototypeBean {
public Object execute() {
//...
}
}
public class SingletonBean {
private PrototypeBean prototypeBean;
@Autowired
public SingletonBean(PrototypeBean prototypeBean) { // Noncompliant, the same instance of PrototypeBean is used for each bean request.
this.prototypeBean = prototypeBean;
}
public Object process() {
return prototypeBean.execute();
}
}
Using the ApplicationContext to retrieve a new instance of a Prototype bean on each bean request.
@Component
@Scope("prototype")
public class PrototypeBean {
public Object execute() {
//...
}
}
public class SingletonBean implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Autowired
public SingletonBean(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public Object process() {
PrototypeBean prototypeBean = createPrototypeBean();
return prototypeBean.execute();
}
protected PrototypeBean createPrototypeBean() {
return this.applicationContext.getBean("prototypeBean", PrototypeBean.class);
}
}