Spring Bean的作用域

  • singleton:单例模式

    IOC容器仅创建一个Bean实例,后续每次从IOC容器中获取的都是同一个Bean实例。Spring中的Bean默认都是单例模式的。

  • prototype:原型模式(又叫做多例模式)

    每次从IOC容器中获取的都是一个新的Bean实例。

  • request:HTTP请求

    每一次HTTP请求都会产生一个新的Bean实例,该Bean实例仅在当前HTTP请求中共享。

  • session:HTTP会话

    每一次HTTP会话都会产生一个新的Bean实例,该Bean实例仅在当前HTTP会话中共享。

  • global-session:全局HTTP会话

    所有的Session共享一个Bean实例。仅在基于portlet的web应用中才有意义,Spring5已经没有了。

单例Bean与原型Bean对比

  • 使用:

    • 单例bean只有第一次创建新的bean(保存在Spring容器中),后面都会复用该bean,所以不会频繁创建对象。
    • 原型bean每次都会新创建。
  • 线程安全:

    • 单例bean非线程安全
    • 原型bean线程安全

对单例bean的误解:

单例bean并不是指Spring容器中只能有一个该类型的bean(该类型可以有多个id不同的bean),而是指根据条件(name、type)从Spring容器中获取bean时,如果容器中有则直接使用该bean,容器中没有才会创建bean。

举例说明:

public class UserService {

}
@Configuration
public class TestConfiguration {

    @Bean("userService1")
    public UserService userService1() {
        return new UserService();
    }

    @Bean("userService2")
    public UserService userService2() {
        return new UserService();
    }
}
@SpringBootApplication
public class SpringbootApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext applicationContext = SpringApplication.run(SpringbootApplication.class, args);
        System.out.println(applicationContext.getBean("userService1", UserService.class));
        System.out.println(applicationContext.getBean("userService2",UserService.class));
        System.out.println(applicationContext.getBean("userService1", UserService.class));
        System.out.println(applicationContext.getBean("userService2",UserService.class));
        System.out.println(applicationContext.getBean("userService1", UserService.class));
        System.out.println(applicationContext.getBean("userService2",UserService.class));
    }

}

打印日志

com.joker.test.autowiredtest.UserService@68cc6319
com.joker.test.autowiredtest.UserService@3d0f4ac4
com.joker.test.autowiredtest.UserService@68cc6319
com.joker.test.autowiredtest.UserService@3d0f4ac4
com.joker.test.autowiredtest.UserService@68cc6319
com.joker.test.autowiredtest.UserService@3d0f4ac4

分析可知,单例UserService在Spring容器中有两个实例bean,重复获取也不会新增实例,而是使用Spring容器中原有的两个实例bean。

单例bean非线程安全

单例bean是存在线程问题的。当多个线程操作同一个对象的时候,对这个对象的非静态成员变量的写操作会存在线程安全问题。

解决办法:

  1. 在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在 ThreadLocal 中(推荐)。
  2. 避免在类中定义可变的成员变量(不推荐,因为不太现实)。
Logo

Authing 是一款以开发者为中心的全场景身份云产品,集成了所有主流身份认证协议,为企业和开发者提供完善安全的用户认证和访问管理服务

更多推荐