Spring 的 Controller 是单例还是多例?

简介: 在笔试面试的时候经常会遇到的一个问题:Spring 的 Controller 是单例还是多例?

1 前言


在笔试面试的时候经常会遇到的一个问题:Spring 的 Controller 是单例还是多例?


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


那么既然不是线程安全的,那么spring怎么保证做到并发的安全性呢?


2 正文


首先我们来看下面的例子:


package com.springboot.springbootdemo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ScopeController {
    public int number = 0;
    @RequestMapping("/text1")
    public int test1(){
        number=number+1;
        return number;
    }
    @RequestMapping("/text2")
    public int test2(){
        number=number+1;
        return ++number;
    }
}
复制代码


e7471f920132463b931f0ad5643e89b5~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


539ed0ab6b5c45a4bb5a549153dc743c~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


可以发现我们首先访问 http://localhost:8080/text1,得到的答案是1;然后我们再访问http://localhost:8080/text2,得到的答案是 2。所以得到的不同的值,证明这是线程不安全的。


说到这个问题,我们就需要说到之前提到的一个问题,那就是关于spring的bean作用域,关于spring的作用域可以参考之前的一篇文章:传送门Spring注解(三):@scope设置组件作用域


在Spring注解开发中@Scope注解可以用于设置组件的作用域,通过@Scope源码,可以发现@Scope注解有五种作用域,即:


SINGLETON:单例模式,默认模式,不写的时候默认是SINGLETON
PROTOTYPE:原型模式
REQUEST:同一次请求则只创建一次实例
SESSION:同一个session只创建一次实例
GLOBAL SESSION:全局的web域,类似于servlet中的application。
复制代码


在默认的情况下,controller是单例模式singleton,单例是不安全的,因为它会导致属性重复使用。


接下来我们再来给controller增加作用多例 @Scope("prototype")


package com.springboot.springbootdemo.controller;
import org.springframework.context.annotation.Scope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Scope("prototype")
public class ScopeController {
    public int number = 0;
    @RequestMapping("/text1")
    public String test1(){
        number=number+1;
        return String.valueOf(number);
    }
    @RequestMapping("/text2")
    public String test2(){
        number=number+1;
        return String.valueOf(number);
    }
}
复制代码


3daff66d5bc5442d9e8b303a0bd4a544~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


b207a4c9fcab4f26bd7c8f70f453c2ba~tplv-k3u1fbpfcp-zoom-in-crop-mark_1304_0_0_0.webp.jpg


可以发现我们首先访问 http://localhost:8080/text1,得到的答案是1;然后我们再访问http://localhost:8080/text2,得到的答案是 1。这时候就是线程安全的了。


3 总结


通过上面的例子,可以得出结论:


1、spring的controller默认是单例模式,而这种模式下是线程不安全的,所以在这种模式下不要在controller种定义成员变量;


2、在单例模式下可以通过使用ThreadLocal 解决线程安全问题。


线程安全问题主要是全局变量和静态变量引起的。若每个线程中对全局变量、静态变量读操作,而无写操作,一般来说这个全局变量是线程安全的。若多个线程同时执行写操作,需要考虑线程同步问题,否则影响线程安全。spring 使用ThreadLocal 实现高并发下 共享资源的同步。


使用ThreadLocal 为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线 程都完全拥有该变量。


ThreadLocal 为每一个变量维护变量的副本的原理如下:


public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}
复制代码


3、通过@Scope(“prototype”)注解,将默认的作用域改为多例模式,这时候就能够在controller种定义非静态的成员变量了。


在spring的controller默认是单例,原因有两点:


(1)为了性能:单例不用每次都创建


(2)不需要多例:只要controller中不定义属性,那么单例完全是安全可用的,如果定义了,那单例肯定会出现竞争访问;非要定义,则通过注解@Scope("prototype"),将其设置为多例模式。

目录
相关文章
|
8月前
|
缓存 算法 安全
Spring 为啥默认把bean设计成单例的?这篇讲的明明白白的
Spring 为啥默认把bean设计成单例的?这篇讲的明明白白的
127 0
|
8月前
|
安全 Java Spring
Spring框架中的单例Bean是线程安全的吗?
Spring框架中的单例Bean是线程安全的吗?
98 1
|
5月前
|
安全 Java C#
Spring创建的单例对象,存在线程安全问题吗?
Spring框架提供了多种Bean作用域,包括单例(Singleton)、原型(Prototype)、请求(Request)、会话(Session)、全局会话(GlobalSession)等。单例是默认作用域,保证每个Spring容器中只有一个Bean实例;原型作用域则每次请求都会创建一个新的Bean实例;请求和会话作用域分别与HTTP请求和会话绑定,在Web应用中有效。 单例Bean在多线程环境中可能面临线程安全问题,Spring容器虽然确保Bean的创建过程是线程安全的,但Bean的使用安全性需开发者自行保证。保持Bean无状态是最简单的线程安全策略;
|
7月前
|
Java Spring 容器
解读spring5源码中实例化单例bean的调用链
解读spring5源码中实例化单例bean的调用链
|
7月前
|
安全 Java Spring
spring的controller是单例还是多例,怎么保证并发的安全
spring的controller是单例还是多例,怎么保证并发的安全
49 0
|
7月前
|
XML JSON Java
图文并茂:解析Spring Boot Controller返回图片的三种方式
图文并茂:解析Spring Boot Controller返回图片的三种方式
651 0
|
8月前
|
安全 Java Spring
Spring 的 Controller 是单例还是多例?怎么保证并发的安全
Spring 的 Controller 是单例还是多例?怎么保证并发的安全
38 0
|
前端开发 Java Spring
SpringMVC之Controller查找(Spring4.0.3/Spring5.0.4源码进化对比)
0 摘要 本文从源码层面简单讲解SpringMVC的处理器映射环节,也就是查找Controller详细过程 1 SpringMVC请求流程 Controller查找在上图中对应的步骤1至2的过程 SpringMVC详细运行流程图 2 SpringMVC初始化过程 2.1 先认识两个类 Handler 通常指用于处理request请求的实际对象,可以类比 XxxController。
1318 0
|
15天前
|
Java 数据库连接 Maven
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
自动装配是现在面试中常考的一道面试题。本文基于最新的 SpringBoot 3.3.3 版本的源码来分析自动装配的原理,并在文未说明了SpringBoot2和SpringBoot3的自动装配源码中区别,以及面试回答的拿分核心话术。
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
|
1天前
|
存储 安全 Java
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
22 8