[源码系列:手写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不同,后者是像一个单一的实例化对象,由容器管理其生命周期。

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.https://github.com/yihuiaa/little-spring

核心代码

BeanDefinition

java 复制代码
    private String scope = SCOPE_SINGLETON;
    private boolean singleton = true;
    private boolean prototype = false;
    public static String SCOPE_SINGLETON = "singleton";
    public static String SCOPE_PROTOTYPE = "prototype";

    public void setScope(String scope) {
        this.scope = scope;
        this.singleton = SCOPE_SINGLETON.equals(scope);
        this.prototype = SCOPE_PROTOTYPE.equals(scope);
    }

    public boolean isSingleton() {
        return this.singleton;
    }
    public boolean isPrototype() {
        return this.prototype;
    }

AbstractAutowireCapableBeanFactory#doCreateBean()

java 复制代码
...	
	protected Object doCreateBean(String beanName, BeanDefinition beanDefinition) {
		Object bean = null;
		try {
			bean = createBeanInstance(beanDefinition);
			//为bean填充属性
			applyPropertyValues(beanName, bean, beanDefinition);
			//执行bean的初始化方法和BeanPostProcessor的前置和后置处理方法
			initializeBean(beanName, bean, beanDefinition);
		} catch (Exception e) {
			throw new BeansException("Instantiation of bean failed", e);
		}

		//注册有销毁方法的bean
		registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);

		if (beanDefinition.isSingleton()) {
			addSingleton(beanName, bean);
		}
		return bean;
	}
...

AbstractAutowireCapableBeanFactory#registerDisposableBeanIfNecessary()

java 复制代码
	/**
	 * 注册有销毁方法的bean,即bean继承自DisposableBean或有自定义的销毁方法
	 *
	 * @param beanName
	 * @param bean
	 * @param beanDefinition
	 */
	protected void registerDisposableBeanIfNecessary(String beanName, Object bean, BeanDefinition beanDefinition) {
		//只有singleton类型bean会执行销毁方法
		if (beanDefinition.isSingleton()) {
			if (bean instanceof DisposableBean || StrUtil.isNotEmpty(beanDefinition.getDestroyMethodName())) {
				registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, beanDefinition));
			}
		}
	}

测试

prototype-bean.xml

XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
	         http://www.springframework.org/schema/beans/spring-beans.xsd
		 http://www.springframework.org/schema/context
		 http://www.springframework.org/schema/context/spring-context-4.0.xsd">

    <bean id="car" class="bean.Car" scope="prototype">
        <property name="name" value="Rolls-Royce"/>
    </bean>

</beans>

单元测试

java 复制代码
public class PrototypeBeanTest {
	@Test
	public void testPrototype() throws Exception {
		ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:prototype-bean.xml");

		Car car1 = applicationContext.getBean("car", Car.class);
		Car car2 = applicationContext.getBean("car", Car.class);
		System.out.println(car1 == car2);;
	}
}

测试结果

bash 复制代码
false
Process finished with exit code 0
相关推荐
handsomestWei11 分钟前
Cursor Java开发配置
java·ai编程·环境配置·cursor
Y40900120 分钟前
Java基础——实现图书管理系统交互功能
java·开发语言·笔记·交互
LiuYaoheng29 分钟前
【Android】使用 Intent 传递对象的两种序列化方式
android·java·笔记·学习
寒水馨44 分钟前
Java 24 新特性解析与代码示例
java·开发语言·新特性·jdk24·java24
奋进的孤狼1 小时前
【Java】在一个前台界面中动态展示多个数据表的字段及数据
java·数据库·oracle
IT利刃出鞘2 小时前
Intellij Idea--解决Cannot download “https://start.spring.io‘: Connect timedout
java·ide·intellij-idea
中国lanwp2 小时前
Spring 全局异常处理机制:多个 @ControllerAdvice 与重复 @ExceptionHandler
java·数据库·spring
Ashlee_code2 小时前
关税战火中的技术方舟:新西兰证券交易所的破局之道 ——从15%关税冲击到跨塔斯曼结算联盟,解码下一代交易基础设施
java·python·算法·金融·架构·系统架构·区块链
怒码ing3 小时前
分布式事务----spring操作多个数据库,事务以及事务回滚还有用吗
数据库·分布式·spring
hqxstudying3 小时前
SpringBoot启动项目详解
java·spring boot·后端