• [源码系列:手写spring] IOC第十三节:Bean作用域,增加prototype的支持


             为了帮助大家更深入的理解bean的作用域,特意将BeanDefinition的双例支持留到本章节中,创建Bean,相关Reader读取等逻辑都有所改动。

    内容介绍

            在Spring中,Bean的作用域(Scope)定义了Bean的生命周期和可见性。包括单例和原型,在本章节中我们将为Bean添加多例的支持,下面是Prototype作用域的几个特征介绍:

    1. 多例(Prototype): Bean的prototype作用域表示每次从容器中获取该Bean时都会创建一个新的实例。每个请求或依赖注入都会导致创建一个全新的Bean对象。

    2. 不受Spring容器管理: prototype作用域的Bean实例不受Spring容器的管理,容器在创建Bean之后不会再跟踪它。这意味着Spring容器不会负责释放或销毁prototype作用域的Bean。一旦Bean被创建并交给调用者,调用者负责Bean的生命周期,包括销毁。

    3. 适用场景: prototype作用域适用于那些每次获取实例时都需要全新状态的Bean,例如,HTTP请求的处理器或线程池中的任务。这样可以确保每次获取的Bean都是独立的,不会影响其他实例。

            这里设计到设计模式中的原型模式,形象地说,可以将prototype作用域的Bean比喻为一个不断复制的模具。每次你需要一个新的Bean实例时,Spring容器会根据模具再次制作一个全新的Bean。这个模具本身不受容器管理,而是由你自己管理它的生命周期。这与singleton作用域的Bean不同,后者是像一个单一的实例化对象,由容器管理其生命周期。
     

    当前bean生命周期
    prototype作用域

            可见prototype类型的bean并不受spring容器的管理,你需要自己负责销毁或释放这些Bean实例,以防止内存泄漏。 

    代码分支

    GitHub - yihuiaa/little-spring: 剖析Spring源码,包括常见特性IOC、AOP、三级缓存等...剖析Spring源码,包括常见特性IOC、AOP、三级缓存等... Contribute to yihuiaa/little-spring development by creating an account on GitHub.icon-default.png?t=N7T8https://github.com/yihuiaa/little-spring

    核心代码

     BeanDefinition

    1. private String scope = SCOPE_SINGLETON;
    2. private boolean singleton = true;
    3. private boolean prototype = false;
    4. public static String SCOPE_SINGLETON = "singleton";
    5. public static String SCOPE_PROTOTYPE = "prototype";
    6. public void setScope(String scope) {
    7. this.scope = scope;
    8. this.singleton = SCOPE_SINGLETON.equals(scope);
    9. this.prototype = SCOPE_PROTOTYPE.equals(scope);
    10. }
    11. public boolean isSingleton() {
    12. return this.singleton;
    13. }
    14. public boolean isPrototype() {
    15. return this.prototype;
    16. }

    AbstractAutowireCapableBeanFactory#doCreateBean()

    1. ...
    2. protected Object doCreateBean(String beanName, BeanDefinition beanDefinition) {
    3. Object bean = null;
    4. try {
    5. bean = createBeanInstance(beanDefinition);
    6. //为bean填充属性
    7. applyPropertyValues(beanName, bean, beanDefinition);
    8. //执行bean的初始化方法和BeanPostProcessor的前置和后置处理方法
    9. initializeBean(beanName, bean, beanDefinition);
    10. } catch (Exception e) {
    11. throw new BeansException("Instantiation of bean failed", e);
    12. }
    13. //注册有销毁方法的bean
    14. registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);
    15. if (beanDefinition.isSingleton()) {
    16. addSingleton(beanName, bean);
    17. }
    18. return bean;
    19. }
    20. ...

    AbstractAutowireCapableBeanFactory#registerDisposableBeanIfNecessary()

    1. /**
    2. * 注册有销毁方法的bean,即bean继承自DisposableBean或有自定义的销毁方法
    3. *
    4. * @param beanName
    5. * @param bean
    6. * @param beanDefinition
    7. */
    8. protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {
    9. //只有singleton类型bean会执行销毁方法
    10. if (beanDefinition.isSingleton()) {
    11. if (bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())) {
    12. registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition));
    13. }
    14. }
    15. }

    测试

    prototype-bean.xml

    1. "1.0" encoding="UTF-8"?>
    2. <beans xmlns="http://www.springframework.org/schema/beans"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xmlns:context="http://www.springframework.org/schema/context"
    5. xsi:schemaLocation="http://www.springframework.org/schema/beans
    6. http://www.springframework.org/schema/beans/spring-beans.xsd
    7. http://www.springframework.org/schema/context
    8. http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    9. <bean id="car" class="bean.Car" scope="prototype">
    10. <property name="name" value="Rolls-Royce"/>
    11. bean>
    12. beans>

    单元测试

    1. public class PrototypeBeanTest {
    2. @Test
    3. public void testPrototype() throws Exception {
    4. ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:prototype-bean.xml");
    5. Car car1 = applicationContext.getBean("car", Car.class);
    6. Car car2 = applicationContext.getBean("car", Car.class);
    7. System.out.println(car1 == car2);;
    8. }
    9. }

    测试结果

    1. false
    2. Process finished with exit code 0

  • 相关阅读:
    网络框架重构之路plain2.0(c++23 without module) 环境
    MySQL索引
    【C++基础】(6)类和对象(下):友元,内部类,初始化列表,静态成员
    开开心心带你学习MySQL数据库之节尾篇
    linux tasks errors
    vue父子页面传值问题
    C++学习 --函数
    Python中的科学计算和数学建模
    关于打代码的一些些心得
    保证数据库质量安全:从0开始的数据测试
  • 原文地址:https://blog.csdn.net/weixin_43848166/article/details/132720521