Backend Development 6 min read

Understanding Spring Controller Scope: Singleton vs Prototype and Best Practices

This article explains why Spring MVC controllers are singleton by default, demonstrates the thread‑unsafe behavior of using instance fields, shows how applying @Scope("prototype") changes the behavior, and provides practical recommendations such as avoiding mutable fields, using prototype scope or ThreadLocal for safe controller design.

Top Architect
Top Architect
Top Architect
Understanding Spring Controller Scope: Singleton vs Prototype and Best Practices

In Spring MVC, a class annotated with @Controller is a singleton bean by default, which means the same instance handles all incoming requests. Using non‑static member variables in such a controller can lead to data corruption because the singleton is not thread‑safe.

The following example illustrates the problem. A controller defines an int num field and two request mappings that increment and print this field.

package com.riemann.springbootdemo.controller;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author riemann
 * @date 2019/07/29 22:56
 */
@Controller
public class ScopeTestController {

    private int num = 0;

    @RequestMapping("/testScope")
    public void testScope() {
        System.out.println(++num);
    }

    @RequestMapping("/testScope2")
    public void testScope2() {
        System.out.println(++num);
    }
}

When the application is run, accessing http://localhost:8080/testScope prints 1 . A subsequent request to http://localhost:8080/testScope2 prints 2 , demonstrating that the same controller instance is shared and the field is mutated across requests.

To make the controller safe, the @Scope("prototype") annotation can be added, turning the bean into a prototype (multi‑instance) bean. The modified class looks like this:

package com.riemann.springbootdemo.controller;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * @author riemann
 * @date 2019/07/29 22:56
 */
@Controller
@Scope("prototype")
public class ScopeTestController {

    private int num = 0;

    @RequestMapping("/testScope")
    public void testScope() {
        System.out.println(++num);
    }

    @RequestMapping("/testScope2")
    public void testScope2() {
        System.out.println(++num);
    }
}

Now each request creates a new controller instance, so both /testScope and /testScope2 return 1 , confirming that the prototype scope eliminates the shared‑state problem.

Practical recommendations:

Avoid defining mutable member variables in controllers.

If a mutable field is unavoidable, declare the controller with @Scope("prototype") to use a new instance per request.

Alternatively, use ThreadLocal variables inside the controller for thread‑confined state.

Spring provides five bean scopes:

singleton : a single instance per Spring ApplicationContext , created eagerly unless lazy-init is used.

prototype : a new instance each time the bean is requested from the container; the container does not manage its full lifecycle after creation.

request : a new instance for each HTTP request (web‑specific).

session : a new instance for each HTTP session.

global session : a global session scope, similar to the servlet application scope.

JavaSpringControllerSingletonPrototypeScope
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.