Understanding Spring Boot Controller Scope: Singleton vs Prototype and Thread Safety
This article explains why Spring MVC controllers are singleton by default, demonstrates the thread‑safety issues caused by non‑static member variables, shows how applying @Scope("prototype") changes behavior, and summarizes the five Spring bean scopes with practical recommendations.
By default, a Spring MVC @Controller is a singleton, so using non‑static member variables can lead to data inconsistency because the instance is shared across threads.
Example code demonstrates the problem:
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 accessing http://localhost:8080/testScope the output is 1 ; a subsequent call to /testScope2 prints 2 , showing that the same controller instance is reused and the state is shared, which is not thread‑safe.
Changing the controller to prototype scope resolves the 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
@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 return 1 on each request, confirming that a new controller instance is created each time.
Key takeaways:
Do not define mutable member variables in a singleton controller.
If a member variable is required, annotate the controller with @Scope("prototype") to make it prototype‑scoped.
Alternatively, use ThreadLocal for thread‑confined data.
Spring bean scopes overview (five types):
singleton : one instance per Spring container (eagerly created unless lazy-init is used).
prototype : a new instance each time getBean is called; Spring does not manage the lifecycle after creation.
request : a new instance for each HTTP request, managed by Spring in web applications.
session : a new instance for each HTTP session.
global session : a single instance for the entire web application, similar to the servlet application scope.
For further reading, a recommended Spring Boot tutorial is available at http://blog.didispace.com/spring-boot-learning-2x/ .
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.