Spring中bean的scope

简介:

Spring容器中的bean具备不同的scope,最开始只有singleton和prototype,但是在2.0之后,又引入了三种类型:request、session和global session,不过这三种类型只能在Web应用中使用。

在定义bean的时候,可以通过指定<bean>的singleton或者scope属性来指定相应对象的scope,例如:

<bean id="testMock" class="org.test.javadu.TestMock" scope="prototype"/>

或者

<bean id="testMock" class="org.test.javadu.TestMock" singleton="false"/>

1. singleton

配置中的bean定义可以看作是一个模板,容器会根据这个模板来构造对象。bean定义中的scope语义会决定:容器将根据这个模板构造多少对象实例,又该让这个对象实例存活多久。标记为拥有singleton scope的对象定义,在Spring的IoC容器中只存在一个对象实例,所有该对象的引用都共享这个实例。该实例从容器启动,并因为第一次被请求而初始化之后,将一直存活到容器退出,也就是说,它与IoC容器“几乎”拥有相同的“寿命”。

下图是Spring参考文档中给出的singleton的bean的实例化和注入语义示意图,或许更能形象得说明问题。


singleton scope

需要注意的是,不要将Spring中的singleton bean的概念和GoF中提出的Singleton模式混淆,二者的语义并不相同:Spring中的singleton scope是指在每个容器中只有一个bean的实例对象;GoF模式中的Singleton指的是在同一个classloader中只有某个Singleton类的一个实例对象。

Spring中的bean默认是singleton的,下面这两种写法的效果相同:

<bean id="accountService" class="com.foo.DefaultAccountService"/>

<!-- the following is equivalent, though redundant (singleton scope is the default) -->
<bean id="accountService" class="com.foo.DefaultAccountService" scope="singleton"/>

2. prototype

针对声明为拥有prototype scope的bean定义,容器在接到该类型对象的请求的时候,会每次都重新生成一个新的实例对象给请求方。虽然这种类型的对象的实例化以及属性设置等工作都是由容器负责的,但是只要准备完毕,并且对象实例返回请求方之后,容器就不再拥有当前返回对象的引用,请求方需要自己负责当前返回对象的后继的声明周期的管理工作,例如该对象的销毁。也就是说,容器每次返回给请求方一个新的实例对象后,就任由这个对象“自生自灭”了。

对于那些请求方不能共享使用的对象类型,应该将其bean定义的scope设置为prototype。这样,每个请求方可以得到自己专有的一个对象实例。通常,声明为prototype的对象都是一些有状态的,比如保存每个顾客信息的对象。

从Spring参考文档下的这幅图片,可以再次了解prototype scope的bean定义,在实例化对象和注入依赖的时候,它的具体语义是什么样子。


prototype scope

用以下两个ban定义的效果是一样的:

<bean id="accountService" class="com.foo.DefaultAccountService" scope="prototype"/>
<bean id="accountService" class="com.foo.DefaultAccountService" singleton="false"/>

实践教训

今天写这个主题的原因是:在最近的项目中由于对于singleton类型的对象没有理解透彻,导致每日定时任务的统计数据出错。

具体描述如下:每天我需要跑一个定时任务,该任务使用多线程方式去执行,每个线程都需要往一个统一的结果集中写数据,基本的代码结构如下:

public class StatisticsTaskManager {
       private static final Logger logger = LoggerFactory.getLogger(OdsManager.class);

       private ConcurrentHashMap<Long, StatisticsBean> resultMap = new ConcurrentHashMap<>();

       /*对外暴露的统计接口*/
       public List<StatisticsBean> getOdsStatisticDataFromUtc() {
           ExecutorService pool = Executors.newFixedThreadPool(80);

           for (int dbIndex = 0; dbIndex < Constants.DB_NUM; dbIndex++) {
               for (int tableIndex = 0; tableIndex < Constants.TABLE_NUM; tableIndex++) {
                      DealUidRunnable thread = new DealUidRunnable(dbIndex, tableIndex);
                      pool.execute(thread);
              }
          }
          pool.shutdown();
          try {
                pool.awaitTermination(6, TimeUnit.HOURS);
          } catch (InterruptedException e) {
                logger.error("error:", e);
          }
          logger.info("finished! num={}", resultMap.size());
          //resultMap.clear();
          return convertMapToList();
      }

      /*具体的任务执行过程*/
      这里会修改resultMap
}

在上述代码片段中,每个线程会判断resultMap中是否有key存在,如果存在则更新对应的bean,如果不存在则新建一个bean。这就要求在每个任务开始执行前,这个resultMap是空的,但是我没有意识到这个resultMap仅仅随着StatisticsTaskManager对象的生成而仅仅初始化一次,后续会作为singleton对象存在于容器中。

修改也非常简单,就是在当天的定时任务执行完之后,调用resultMap.clear()将结果map中的数据清除即可。

参考资料

  1. 《揭秘Spring》
  2. Spring官方文档

    相关文章
    |
    25天前
    |
    XML 安全 Java
    |
    1天前
    |
    存储 Java Spring
    【Spring】获取Bean对象需要哪些注解
    @Conntroller,@Service,@Repository,@Component,@Configuration,关于Bean对象的五个常用注解
    |
    1天前
    |
    存储 Java 应用服务中间件
    【Spring】IoC和DI,控制反转,Bean对象的获取方式
    IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
    |
    7天前
    |
    XML Java 数据格式
    Spring容器Bean之XML配置方式
    通过对以上内容的掌握,开发人员可以灵活地使用Spring的XML配置方式来管理应用程序的Bean,提高代码的模块化和可维护性。
    37 6
    |
    9天前
    |
    XML Java 数据格式
    🌱 深入Spring的心脏:Bean配置的艺术与实践 🌟
    本文深入探讨了Spring框架中Bean配置的奥秘,从基本概念到XML配置文件的使用,再到静态工厂方式实例化Bean的详细步骤,通过实际代码示例帮助读者更好地理解和应用Spring的Bean配置。希望对你的Spring开发之旅有所助益。
    56 3
    |
    4月前
    |
    XML Java 数据格式
    Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
    这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
    Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
    |
    1月前
    |
    缓存 Java Spring
    实战指南:四种调整 Spring Bean 初始化顺序的方案
    本文探讨了如何调整 Spring Boot 中 Bean 的初始化顺序,以满足业务需求。文章通过四种方案进行了详细分析: 1. **方案一 (@Order)**:通过 `@Order` 注解设置 Bean 的初始化顺序,但发现 `@PostConstruct` 会影响顺序。 2. **方案二 (SmartInitializingSingleton)**:在所有单例 Bean 初始化后执行额外的初始化工作,但无法精确控制特定 Bean 的顺序。 3. **方案三 (@DependsOn)**:通过 `@DependsOn` 注解指定 Bean 之间的依赖关系,成功实现顺序控制,但耦合性较高。
    实战指南:四种调整 Spring Bean 初始化顺序的方案
    |
    23天前
    |
    安全 Java 开发者
    Spring容器中的bean是线程安全的吗?
    Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
    32 1
    |
    2月前
    |
    XML Java 数据格式
    Spring从入门到入土(bean的一些子标签及注解的使用)
    本文详细介绍了Spring框架中Bean的创建和使用,包括使用XML配置文件中的标签和注解来创建和管理Bean,以及如何通过构造器、Setter方法和属性注入来配置Bean。
    83 9
    Spring从入门到入土(bean的一些子标签及注解的使用)
    |
    3月前
    |
    缓存 安全 Java
    Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
    从底层源码入手,通过代码示例,追踪AnnotationConfigApplicationContext加载配置类、启动Spring容器的整个流程,并对IOC、BeanDefinition、PostProcesser等相关概念进行解释
    335 24