Spring Bean 的作用域定义了 Spring 容器如何创建和管理 Bean 的实例,它决定了一个 Bean 实例的生命周期以及它如何在应用中被共享。
在 Spring 中,Bean 常见的作用域有 5 中:
singleton
:单例作用域prototype
:原型作用域request
:请求作用域session
:会话作用域application
:全局作用域
注意 :后 3 种作用域,只适用于 Web 应用程序。
1、单例作用域:
singleton
单例作用域是 Spring 的默认作用域,在这个作用域中,Spring 容器为每个在 Spring 配置中定义的 Bean 创建一个且只创建一个实例,这意味着无论你在应用中的哪个位置请求这个 Bean,总是获得相同的实例。写个简单的例子演示下:
@Data
@ToString
public class User {
private String userName;
private Integer age;
}
@Component
public class UserService {
@Bean
public User getUser() {
User user = new User();
user.setUserName("大明哥 1 号");
user.setAge(18);
return user;
}
}
再定义一个 Controller 访问:
@RestController
public class TestController {
@Autowired
private User user;
@GetMapping("/getUser1")
public String getUser1() {
System.out.println("getUser1 - 修改之前的 user:" + user.toString());
user.setUserName("大明哥 2 号");
System.out.println("getUser1 - 修改之后的 user:" + user.toString());
return "getUser1";
}
@GetMapping("/getUser2")
public String getUser2() {
System.out.println("getUser2 - 修改之后的 user:" + user.toString());
return "getUser2";
}
}
getUser1()
先打印,然后再修改 user 对象,getUser2()
直接打印,我们先请求 getUser1()
然后再请求 getUser2()
,结果如下 :
getUser1 - 修改之前的 user:User(userName=大明哥 1 号, age=18)
getUser1 - 修改之后的 user:User(userName=大明哥 2 号, age=18)
getUser2 - 修改之后的 user:User(userName=大明哥 2 号, age=18)
你甚至可以将 getUser1()
和 getUser2()
方法放在不同的 Controller 中演示,得到的结果也和上面一样。
singleton 作用域适用于绝大多数情况,特别是当 Bean 是无状态的时候。
- 原型作用域:
prototype
在 prototype
作用域中,Spring 容器每次被请求时都会创建一个全新的 Bean 实例,这意味着每个通过 Spring 容器获取的 Prototype Bean 都是一个全新的实例。
调整 UserService 的代码,如下:
@Component
public class UserService {
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public User getUser() {
//...
}
}
将作用域调整为 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
。
同时要将 getUser1()
和 getUser2()
两个方法分开放到不同的 Controller 中,否则你会得到和上面一样的结果,原因是因为 Controller 也是单例模式,意味着只会初始化一次。也可以通过 applicationContext.getBean
方法获取 User。
执行结果:
getUser1 - 修改之前的 user:User(userName=大明哥 1 号, age=18)
getUser1 - 修改之后的 user:User(userName=大明哥 2 号, age=18)
getUser2 - 修改之后的 user:User(userName=大明哥 1 号, age=18)
prototype
作用域由于每次得到都是一个全新的对象实例,所以使用他会带来一些挑战:
- 性能考虑:频繁创建和销毁大量的
prototype
实例可能会对系统性能产生影响。 - 管理责任:
prototype
作用域的 Bean 其生命周期并不受 Spring 容易管理,因此实例的销毁和资源回收是调用者的责任。
3、请求作用域:
request
request
作用域确保每次 HTTP 请求都会创建一个新的 Bean 实例。这意味着在同一个请求内部,所有对特定 Bean 的引用都会返回同一个实例,而在不同请求之间则是完全独立的。
而且每个 request
作用域的 Bean 与单个 HTTP 请求的生命周期绑定,当请求结束时,bean 实例也会被销毁。
4、会话作用域:
session
session
作用域确保每个 HTTP 会话都会有一个独立的 Bean 实例。这意味着每个用户的会话都会拥有自己的 Bean 实例,而这些实例彼此间是隔离的。
对于在同一个会话中的多个请求,它们都会访问到相同的 Session 作用域 Bean 实例。
request
作用域的 Bean 的生命周期也是与单个 HTTP 会话的生命周期绑定。
5、会话作用域:
application
application
作用域的 Bean 在整个 web 应用范围内共享,即它们在应用的所有会话和请求之间共享。这种作用域非常适合于全局配置、共享服务或全应用级别的数据。
注意:Spring 容器不负责任何作用域 Bean 的线程安全,所有作用域也无法保证线程安全,在实际应用中我们需要根据具体的应用场景来选择不同的作用域,并确保线程安全。
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。