2024-12-18  阅读(21)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://www.skjava.com/mianshi/baodian/detail/1506848975

Spring Bean 的作用域定义了 Spring 容器如何创建和管理 Bean 的实例,它决定了一个 Bean 实例的生命周期以及它如何在应用中被共享。

在 Spring 中,Bean 常见的作用域有 5 中:

  1. singleton:单例作用域
  2. prototype:原型作用域
  3. request:请求作用域
  4. session:会话作用域
  5. 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 是无状态的时候。

  1. 原型作用域: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 作用域由于每次得到都是一个全新的对象实例,所以使用他会带来一些挑战:

  1. 性能考虑:频繁创建和销毁大量的 prototype 实例可能会对系统性能产生影响。
  2. 管理责任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] ,回复【面试题】 即可免费领取。

阅读全文