Spring容器中scope为prototype类型Bean的回收机制

发布时间:2023年12月19日

一、背景

最近做 DDD 实践时,遇到业务对象需要交给 Spring 管理才能做一些职责内事情。假设账号注册邮箱应用层代码流程:

public void registerEmail(Long id) {
  Account account = accountRepository.findById(id);
  account.registerEmail();
}

其中业务对象 Account 表示账号聚合:

@Component
@Scope(value = BeanDefinition.SCOPE_PROTOTYPE)
public class Account {
  
  @Autowire
  private EmailService emailService;
  
  private String id;
 
  private String name;
  
  // 其他账号属性
  
  // 注册邮箱
  public void registerEmail() {
    //....
    emailService.register();
  }
}

负责 Account 的仓储服务 AccountRepository:

@Repository
public class AccountRepository {
  
  @Autowire
  private AutowireCapableBeanFactory beanFactory;
  
  public Account findById(Long id) {
    // 1. 从 DB 查询
    AccountDO accountDO = accountMapper.findById(id);
    
    // 2. 转换成业务对象
    Account account = convert(accountDO);
    
    // 3. 交给 Spring 管理
    beanFactory.autowireBean(account);
    return account;
  }
}

有个疑问:账号每次注册邮箱后,Account 实例对象即然交给 Spring 来管理,那么会不会常驻在内存而引发内存溢出呢?

二、AutowireCapableBeanFactory 方法 autowireBean 分析

在这里插入图片描述

直接看方法签名:

Populate the given bean instance through applying after-instantiation callbacks and bean property post-processing (e.g. for annotation-driven injection). (翻译:通过实例化后回调和 bean 属性后处理来填充指定 bean 实例)

Note: This is essentially intended for (re-)populating annotated fields and methods, either for new instances or for deserialized instances. It does not imply traditional by-name or by-type autowiring of properties;(翻译:无论是新实例还是反序列化实例,本质上是为了(重新)填充带注解的字段和方法。 它并不意味着传统的按名称或按类型自动装配属性。)

从翻译字面意思上:该方法作用只是对指定对象进行属性填充,尤其是使用注解标注的属性。 再深入到源码:

在这里插入图片描述

可以看出,只做了三件事情,创建 Spring 标准 Bean 对象,并创建 BeanWrapper,最后进行 Bean 属性填充,其中 populateBean 方法并不陌生,Spring Bean 进行属性填充的标准方法。上述源码中创建 existingBean 的 BeanDefinition 时,同时设置了属性为原型(BeanDefinition.SCOPE_PROTOTYPE),也即意味着 Spring 对 existingBean 的管理同原型 Bean 的方式一样(从这里也可以看出 Account 类标记的 @Component 和 @Scope 注解可以去除,笔者已验证)。

三、Spring 容器中 scope 为 prototype 类型 Bean 的回收机制

想要弄明白 Spring Bean 会不会被 JVM 正常回收,要看是否会被 Spring 容器持有,所以要从 Bean 创建流程入手。直接看org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean 方法(源码比较简单,不做深入分析,直接说结论)可知,如果 Bean 实例是 singleton 的,会从 DefaultSingletonBeanRegistry 的 singletonObjects 属性中获取,如果获取不到,就会创建 Bean 实例存放到 singletonObjects 属性中:

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

	/** Logger available to subclasses */
	protected final Log logger = LogFactory.getLog(getClass());

	/** Cache of singleton objects: bean name --> bean instance */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
  
  //......
  
}

DefaultListableBeanFactory 是 Spring IOC 容器的实现,归 Spring 容器管理,自然单例 Bean 也归 Spring 容器管理,所以说正常作用域为 singleton 的 Bean,其生命周期会长期被 Spring 容器管理,直到 Spring 容器被销毁。而作用域为 prototype 的 Bean 和正常 Java 对象一样 new 出来,使用完就会被 JVM 回收。

在这里插入图片描述

四、总结

通过 AutowireCapableBeanFactory 方法 autowireBean 可以将对象装配成 Spring 管理的标准 Bean 对象,主要是用于来填充有注解的属性,这样才可以使用 Spring 的 DI 特性。通过代码 new 出来的对象使用 AutowireCapableBeanFactory 方法 autowireBean 填充属性成为标准 Spring Bean 后不用担心内存溢出的问题,本质上和 Spring prototype Bean 的回收机制一样,使用完就有可能被 JVM 回收掉。

文章来源:https://blog.csdn.net/zhuqiuhui/article/details/135033248
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。