Spring 中的 Bean 有几种作用域?

 作者简介:大家好,我是码炫码哥,前中兴通讯、美团架构师,现任某互联网公司CTO,兼职码炫课堂主讲源码系列专题


代表作:《jdk源码&多线程&高并发》,《深入tomcat源码解析》,《深入netty源码解析》,《深入dubbo源码解析》,《深入springboot源码解析》,《深入spring源码解析》,《深入redis源码解析》等


联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬。码炫课堂的个人空间-码炫码哥个人主页-面试,源码等

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 的线程安全,所有作用域也无法保证线程安全,在实际应用中我们需要根据具体的应用场景来选择不同的作用域,并确保线程安全。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值