当前位置: 首页 > news >正文

【Spring】Bean的作用域(单例、多例、请求、会话、Application)

文章目录

  • 概念
  • Bean 的作用域
    • 代码测试
    • 观察 Bean 的作用域
      • 1. **单例作用域**
      • 2. **多例作用域**
      • 3. **请求作用域**
      • 4. **会话作用域**
      • 5. **`Application` 作用域**

概念

Spring Ioc&DI 戒断,我们学习了 Spring 是如何帮助我们管理对象的

  1. 通过 @Controller@Service@Repository@Component@Configuration@Bean 来声明 Bean 对象
  2. 通过 ApplicationContext 或者 BeanFactory 来获取对象
  3. 通过 @AutowiredSetter 方法或者构造方法等来为应用程序注入所依赖的 Bean 对象

我们来简单回顾一下

  1. 通过 @Bean 声明 bean,把 bean 存在 Spring 容器中
public class Dog {  private String name;  public void setName(String name) {  this.name = name;  }  
}
@Component  
public class DogBeanConfig {  @Bean  public Dog dog() {  Dog dog = new Dog();  dog.setName("旺旺");  return dog;  }  
}
  1. Spring 容器中获取 Bean
    • 通过在代码中直接注入 ApplicationContext 的方式
@SpringBootTest  
class ApplicationTests {  @Autowired  private ApplicationContext applicationContext;  @Test  void contextLoads() {  DogBeanConfig dog1 = applicationContext.getBean(DogBeanConfig.class);;  System.out.println(dog1);  }  }

司改代码,从 Spring 容器中多次获取 Bean

@SpringBootTest  
class ApplicationTests {  @Autowired  private ApplicationContext applicationContext;  @Test  void contextLoads() {  DogBeanConfig dog1 = applicationContext.getBean(DogBeanConfig.class);;  System.out.println(dog1);  DogBeanConfig dog2 = applicationContext.getBean(DogBeanConfig.class);;  System.out.println(dog2);  }  
}

观察运行结果image.png

发现输出的 bean 对象地址是一样的,说明每次从 Spring 容器中取出来的对象都是同一个

  • 这也是“单例模式
  • 单例模式:确保一个类只有一个实例,多次创建也不会创建出多个实例

Bean 的作用域是值 BeanSpring 框架中的某种行为模式

比如单例作用域:表示 Bean 在整个 Spring 中只有一份,它是全局共享的。那么当其他人修改了这个值之后,那么另一个人读到的就是被修改后的值

修改上述代码,给 UserController 添加属性 name

@SpringBootTest  
class ApplicationTests {  @Autowired  private ApplicationContext applicationContext;  @Test  void contextLoads() {  Dog dog1 = applicationContext.getBean(Dog.class);  dog1.setName("狗狗1");  System.out.println(dog1);  System.out.println(dog1.getName());  Dog dog2 = applicationContext.getBean(Dog.class);;  System.out.println(dog2);  System.out.println(dog2.getName());  }  
}

观察运行结果:image.png

  • dog1dog2 为同一个对象,dog2 拿到了 dog1 设置的值

那能不能将 bean 对象设置为非单例的(每次获取的 bean 都是一个新对象呢)

  • 这就是 Bean 的不同作用域了

Bean 的作用域

Spring 中支持 6 中作用域,后 4 种在 Spring MVC 环境才生效

  1. singleton:单例作用域
    • 每个 Spring Ioc 容器内同名称的 Bean 只有一个实例(单例)(默认)
  2. prototype:原型作用域(多例作用域)
    • 每次使用该 bean 时会创建新的实例(非单例)
  3. request:请求作用域
    • 每个 HTTP 请求生命周期内,创建新的实例(Web 环境中,了解)
  4. session:会话作用域
    • 每个 HTTP Session 生命周期内,创建新的实例(Web 环境中,了解)
  5. Application:全局作用域
    • 每个 ServletContext 生命周期里内,创建新的实例(web 环境中,了解)
  6. websocketHTTP WebSocket 作用域
    • 每个 WebSocket 生命周期内,创建新的实例(web 环境中,了解)

我们来看简单的代码实现

定义几个不同作用域的 bean

@Component  
public class DogBeanConfig {  @Bean  public Dog dog() {  Dog dog = new Dog();  dog.setName("旺旺");  return dog;  }  @Bean  @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)  public Dog singleDog() {  Dog dog = new Dog();  return dog;  }  @Bean  @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)  public Dog prototype() {  Dog dog = new Dog();  return dog;  }  @Bean  @RequestScope    public Dog requestDog() {  Dog dog = new Dog();  return dog;  }  @Bean  @SessionScope    public Dog sessionDog() {  Dog dog = new Dog();  return dog;  }  @Bean  @ApplicationScope    public Dog applicationDog() {  Dog dog = new Dog();  return dog;  }  
}
  • @RequestScope 等同于 @Scope (value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
  • @SessionScope 等同于 @Scope (value =WebApplicationContext.SCOPE_SESSION, proxyMode =ScopedProxyMode.TARGET_CLASS)
  • @ApplicationScope 等同于 @Scope (value =WebApplicationContext. SCOPE_APPLICATION, proxyMode =ScopedProxyMode. TARGET_CLASS)

proxyMode ⽤来为 springbean 设置代理 .proxyMode = ScopedProxyMode.TARGET_CLASS 表⽰这个 Bean 基于 CGLIB 实现动态代理,Requestsessionapplication 作⽤域的 Bean 需要设置 proxyMode


代码测试

测试不同作用域的 bean 取到的对象是否一样

@RestController  
public class DogController {  @Autowired  private Dog singleDog;  @Autowired  private Dog prototypeDog;  @Autowired  private Dog requestDog;  @Autowired  private Dog sessionDog;  @Autowired  private Dog applicationDog;  @Autowired  private ApplicationContext applicationContext;  @RequestMapping("/single")  public String single(){  Dog contexDog = (Dog) applicationContext.getBean("singleDog");  return "dog: " + singleDog.toString() + ", contextDog: " + contexDog;  }  @RequestMapping("/prototype")  public String prototype() {  Dog contextDog = (Dog) applicationContext.getBean("prototypeDog");  return "dog: " + prototypeDog.toString() + ", contextDog: " + contextDog;  }  @RequestMapping("/request")  public String request(){  Dog contexDog = (Dog) applicationContext.getBean("requestDog");  return "dog: " + requestDog.toString() + ", contextDog: " + contexDog;  }  @RequestMapping("/session")  public String session() {  Dog contextDog = (Dog) applicationContext.getBean("sessionDog");  return "dog: " + sessionDog.toString() + ", contextDog: " + contextDog;  }  @RequestMapping("/application")  public String application() {  Dog contextDog = (Dog) applicationContext.getBean("applicationDog");  return "dog: " + applicationDog.toString() + ", contextDog: " + contextDog;  }  
}
  • 每个请求都获取两次 Bean
  • @AutowiredapplicationContext.getBean("singleDog") 都是从 Spring 容器中获取对象

观察 Bean 的作用域

1. 单例作用域

地址: http://127.0.0.1:8080/single

多次访问,得到的都是同一个对象,并且 @AutowiredapplicationContext.getBean() 也是同一个对象image.png


2. 多例作用域

地址: http://127.0.0.1:8080/prototype

观察 ContextDog,每次获取的对象都不一样(注入的对象在 Spring 容器启动时,就已经注入了,所以多次请求访问也不会发生变化)image.png


3. 请求作用域

地址: http://127.0.0.1:8080/request

在一次请求中,@AutowiredapplicationContext.getBean() 也是同一个对象,但是每次请求,都会重新创建对象image.png


4. 会话作用域

地址: http://127.0.0.1:8080/session

在一个 session 中,多次请求,获取到的对象都是同一个image.png

换一个浏览器访问,发现会重新创建对象(另一个 sessionimage.png

5. Application 作用域

地址: http://127.0.0.1:8080/application

在一个应用中,多次访问都是同一个对象image.png

  • Application scope 就是对于整个 web 容器来说,bean 的作用域是 ServletContext 级别的
  • 这个和 singleton 有点类似,区别在于:Application scopeServletContext 的单例,singleton 是一个 ApplicationContext 的单例
  • 在一个 web 容器中 ApplicationContext 可以有多个(了解即可)
http://www.lryc.cn/news/609287.html

相关文章:

  • ICCV 2025 | EPD-Solver:西湖大学发布并行加速扩散采样算法
  • Azure DevOps — Kubernetes 上的自托管代理 — 第3部分
  • Autoswagger:揭露隐藏 API 授权缺陷的开源工具
  • Stream 过滤后修改元素,却意外修改原列表
  • 人工智能之数学基础:几何型(连续型)随机事件概率
  • Java开发中敏感信息加密存储全解析:筑牢数据安全防线
  • SpringBoot之整合MyBatisPlus
  • linux火焰图
  • javaweb开发之Servlet笔记
  • 大模型中的Token和Tokenizer:核心概念解析
  • 业务系统跳转Nacos免登录方案实践
  • 电力电子技术知识总结-----PWM知识点
  • 【MybatisPlus】join关联查询MPJLambdaWrapper
  • Javaweb————Windows11系统和idea2023旗舰版手动配置Tomcat9全流程解析
  • 性能测试工具ApacheBench、Jmeter
  • ospf笔记和 综合实验册
  • 在Ansys Mechanical中对磨损进行建模
  • 重生之我在10天内卷赢C++ - DAY 10
  • 分布式文件系统05-生产级中间件的Java网络通信技术深度优化
  • STM32F103_Bootloader程序开发13 - 巧用逆向拷贝,实现固件更新的“准原子”操作,无惧升级中的意外掉电
  • Ethereum: 了解炙手可热 Layer 2 解决方案 Base
  • Spring AOP_2
  • Python 小数据池(Small Object Pool)详解
  • NX969NX972美光固态闪存NX975NX977
  • 深度学习中的三种Embedding技术详解
  • Maven - 依赖的生命周期详解
  • MySQL深度理解-MySQL锁机制
  • vllm0.8.5:思维链(Chain-of-Thought, CoT)微调模型的输出结果包括</think>,提供一种关闭思考过程的方法
  • Remix框架:高性能React全栈开发实战
  • 音视频学习(四十九):音频有损压缩