Backend Development 5 min read

Understanding Spring Controller Scope and Thread Safety

The article explains why Spring MVC controllers are singleton by default, demonstrates the resulting thread‑unsafe behavior with example code, shows how applying @Scope("prototype") makes them prototype scoped, and provides best‑practice recommendations and a summary of Spring bean scopes.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Understanding Spring Controller Scope and Thread Safety

Spring MVC controllers are singleton by default; using non‑static member variables can cause data logic confusion because the singleton is not thread‑safe.

controller默认是单例的,不要使用非静态的成员变量,否则会发生数据逻辑混乱。正因为单例所以不是线程安全的。

Below is a simple test demonstrating this issue:

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

Visiting http://localhost:8080/testScope prints 1 ; then visiting http://localhost:8080/testScope2 prints 2 , showing the singleton controller is not thread‑safe.

Solution: Use Prototype Scope

Adding @Scope("prototype") to the controller makes each request obtain a new instance:

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 both /testScope and /testScope2 start with num = 0 , each printing 1 , confirming prototype scope eliminates the thread‑unsafe behavior.

单例是不安全的,会导致属性重复使用。

Recommendations

Do not define member variables in controllers.

If a non‑static member variable is necessary, annotate the controller with @Scope("prototype") to use prototype scope.

Consider using ThreadLocal variables inside controllers when appropriate.

Supplementary Information: Spring Bean Scopes

Spring defines five main bean scopes:

singleton : one instance per Spring IoC container (eagerly instantiated unless lazy-init is used).

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

request : (web only) a new instance per HTTP request, with Spring still managing the bean thereafter.

session : (web only) a new instance per HTTP session.

global session : (web only) a global session scope similar to the servlet application context.

SpringThread SafetyControllerBeansingletonPrototypeScope
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

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.