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.

Why is this an issue?

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.

What is the potential impact?

When a Singleton bean has a dependency on a bean with a shorter-lived scope, it can lead to the following issues:

How to fix it

Inject a shorter-lived bean into a Singleton bean using ApplicationContext, Factories or Providers.

Code examples

Noncompliant code example

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;
    }
}

Compliant solution

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();
    }
}

Noncompliant code example

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();
    }
}

Compliant solution

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);
    }
}

Resources

Documentation

Articles & blog posts