前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring Ioc 之 Bean的加载(三):各个 scope 的 Bean 创建

Spring Ioc 之 Bean的加载(三):各个 scope 的 Bean 创建

原创
作者头像
周同学
修改2019-09-11 14:30:24
6170
修改2019-09-11 14:30:24
举报
文章被收录于专栏:一块自留地

在Spring中Bean有许多不同的作用域,例如:singleton、prototype、request等等,本篇文章就来分析一下各个scope的Bean是怎么创建的

一、singleton

代码:

代码语言:txt
复制
// Create bean instance.
	//创建单例Bean
	if (mbd.isSingleton()) {
	//这里使用了一个匿名内部类,创建Bean实例对象,并且注册给所依赖的对象
	sharedInstance = getSingleton(beanName, () -> {
		try {
			//创建一个指定Bean实例对象,如果有父级继承,则合并子类和父类的定义
			return createBean(beanName, mbd, args);
		}
		catch (BeansException ex) {
			// Explicitly remove instance from singleton cache: It might have been put there
			// eagerly by the creation process, to allow for circular reference resolution.
			// Also remove any beans that received a temporary reference to the bean.
			//显式地从容器单例模式Bean缓存中清除实例对象
			destroySingleton(beanName);
			throw ex;
		}
	});
	//获取给定Bean的实例对象
	bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
	}
  • 这里使用了匿名内部类,先通过createBean(beanName, mbd, args)方法获取一个 ObjectFactory
  • 把 ObjectFactory 作为参数传入 getSingleton(beanName,objectFactory)方法
  • 使用 getSingleton(beanName,objectFactory) 方法返回的 sharedInstance 作为参数传入 getObjectForBeanInstance(sharedInstance, name, beanName, mbd)来回去最终的Bean实例(详情见Spring Ioc 之 Bean的加载(一)

createBean(beanName, mbd, args)方法比较复杂,在之后的文章中会详细分析,这里就先略过,直接看

getSingleton(beanName,objectFactory)方法。

代码语言:txt
复制
// DefaultSingletonBeanRegistry.java

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
	Assert.notNull(beanName, "Bean name must not be null");
//全局加锁
synchronized (this.singletonObjects) {
	// 从缓存中获取单例bean
	// 因为 singleton 模式其实就是复用已经创建的 bean 所以这步骤必须检查
	Object singletonObject = this.singletonObjects.get(beanName);
	if (singletonObject == null) {
		//是否正在销毁该bean
		if (this.singletonsCurrentlyInDestruction) {
			throw new BeanCreationNotAllowedException(beanName,
					"Singleton bean creation not allowed while singletons of this factory are in destruction " +
					"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
		}
		if (logger.isDebugEnabled()) {
			logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
		}
		// 加载前置处理
		beforeSingletonCreation(beanName);
		boolean newSingleton = false;
		boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
		if (recordSuppressedExceptions) {
			this.suppressedExceptions = new LinkedHashSet<>();
		}
		try {
			// 初始化 bean
			// 这个过程其实是调用 createBean() 方法
			singletonObject = singletonFactory.getObject();
			newSingleton = true;
		}
		catch (IllegalStateException ex) {
			// Has the singleton object implicitly appeared in the meantime ->
			// if yes, proceed with it since the exception indicates that state.
			singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				throw ex;
			}
		}
		catch (BeanCreationException ex) {
			if (recordSuppressedExceptions) {
				for (Exception suppressedException : this.suppressedExceptions) {
					ex.addRelatedCause(suppressedException);
				}
			}
			throw ex;
		}
		finally {
			if (recordSuppressedExceptions) {
				this.suppressedExceptions = null;
			}
			//后置处理
			afterSingletonCreation(beanName);
		}
		if (newSingleton) {
			//加入缓存中
			addSingleton(beanName, singletonObject);
		}
	}
	return singletonObject;
	}
	}

在这段代码中,其实主要是做了一些准备和预处理步骤,真正创建Bean是在singletonFactory.getObject()方法实现的,而 singletonFactory 是由createBean()方法创建后回调的参数。

那么这段代码主要做的事情是什么呢?

  • 尝试从缓存中获取单例Beanundefined 如果已经加载了则直接返回,否则开始加载过程
  • 加载前置处理
  • 获取Bean实例
  • 后置处理
  • 加入缓存
1.1、加载前置处理

beforeSingletonCreation(beanName)是个标记方法,我们来看代码:

代码语言:txt
复制
// 用于添加标志,当前 bean 正处于创建中
	protected void beforeSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			//添加失败,抛出异常
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}

把 beanName 添加到 singletonsCurrentlyInCreationmap中,用来表示该单例bean正在创建,如果添加失败,抛出异常。

1.2、获取Bean实例

通过createBean(beanName)方法返回的 singletonFactory 获取Bean。

1.3、后置处理

afterSingletonCreation(beanName)同样是个表示方法:

代码语言:txt
复制
// 用于移除标记,当前 Bean 不处于创建中
	protected void afterSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
			//移除失败,抛出异常
			throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
		}
	}

创建Bean之后移除创建标示。

前置处理和后置处理的这个创建标示,会在调用isSingletonCurrentlyInCreation(String beanName)时用到,该方法用来判断当前bean是否已经在创建中。

1.4、加入缓存

直接看代码:

代码语言:txt
复制
protected void addSingleton(String beanName, Object singletonObject) {
	synchronized (this.singletonObjects) {
		this.singletonObjects.put(beanName, singletonObject);
		this.singletonFactories.remove(beanName);
		this.earlySingletonObjects.remove(beanName);
		this.registeredSingletons.add(beanName);
	    }
	}

一个 put、一个 add、两个 remove 操作。

  • 【put】singletonObjects 属性,单例 bean 的缓存。
  • 【remove】singletonFactories 属性,单例 bean Factory 的缓存。
  • 【remove】earlySingletonObjects 属性,“早期”创建的单例 bean 的缓存。
  • 【add】registeredSingletons 属性,已经注册的单例缓存。

二、Prototype

代码:

代码语言:txt
复制
        //创建多例Bean
	else if (mbd.isPrototype()) {
		// It's a prototype -> create a new instance.
		//原型模式(Prototype)是每次都会创建一个新的对象
		Object prototypeInstance = null;
		try {
			//加载前置处理,默认的功能是注册当前创建的原型对象
			beforePrototypeCreation(beanName);
			//创建指定Bean对象实例
			prototypeInstance = createBean(beanName, mbd, args);
		}
		finally {
			//加载后置处理,默认的功能告诉IOC容器指定Bean的原型对象不再创建
			afterPrototypeCreation(beanName);
		}
		//获取给定Bean的实例对象
		bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
	}

原型模式很简单,直接创建一个新的实例就好了,不再从缓存中去获取。

beforePrototypeCreation(beanName)前置处理,将当前bean标记为正在创建的原型。

afterPrototypeCreation(beanName)后置处理,取消当前bean的正在创建标示。

调用getObjectFrBeanInstance()方法获取最终bean。(详情见Spring Ioc 之 Bean的加载(一)

三、其他作用域

代码语言:txt
复制
//要创建的Bean既不是Singleton也不是Prototype
	//如:request、session、application等生命周期
	else {
		String scopeName = mbd.getScope();
		final Scope scope = this.scopes.get(scopeName);
		//Bean定义资源中没有配置生命周期范围,则Bean定义不合法
		if (scope == null) {
			throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
		}
		try {
			//这里又使用了一个匿名内部类,获取一个指定生命周期范围的实例
			Object scopedInstance = scope.get(beanName, () -> {
				//前置处理
				beforePrototypeCreation(beanName);
				try {
					return createBean(beanName, mbd, args);
				}
				finally {
					//后置处理
					afterPrototypeCreation(beanName);
				}
			});
			//获取给定Bean的实例对象
			bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
		}
		catch (IllegalStateException ex) {
			throw new BeanCreationException(beanName,
					"Scope '" + scopeName + "' is not active for the current thread; consider " +
					"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
					ex);
		}
	}

分为以下几个步骤:

  • 从Scope注解中获取scope名称
  • 前置处理
  • createBean()
  • 后置处理
  • scope.get()获取bean
  • getObjectForBeanInstance()方法获取Bean

核心流程与原型模式一样,只不过这里调用了scope.get()来获取bean。

代码语言:txt
复制
Object get(String name, ObjectFactory<?> objectFactory);

scope.get()是一个接口,它有多种实现类:

image.png
image.png

我们看一下spring自带的一个实现 SimpleThreadScope:

代码语言:txt
复制
//SimpleThreadScope.java

private final ThreadLocal<Map<String, Object>> threadScope =
			new NamedThreadLocal<Map<String, Object>>("SimpleThreadScope") {
				@Override
				protected Map<String, Object> initialValue() {
					return new HashMap<>();
				}
			};


	//获取bean的实例
	@Override
	public Object get(String name, ObjectFactory<?> objectFactory) {
		// 获取 scope 缓存
		Map<String, Object> scope = this.threadScope.get();
		Object scopedObject = scope.get(name);
		if (scopedObject == null) {
			scopedObject = objectFactory.getObject();
			// 加入缓存
			scope.put(name, scopedObject);
		}
		return scopedObject;
	}

其他scope的实现就不一一去看了,感兴趣的朋友可以自己看一下。

代码语言:txt
复制
总结

上面的代码中有2个重要方法:

  • createBean(beanName, mbd, args)
  • getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd)

这2个方法在3个代码分支中都用到了,createBean下篇文章会详细分析,getObjectForBeanInstance方法在Spring Ioc 之 Bean的加载(一)中已经分析过了。

这里再引用下《Spring 源码深度解析》对该方法的分析:

这个方法主要是验证以下我们得到的 bean 的正确性,其实就是检测当前 bean 是否是 FactoryBean 类型的 bean 。

如果是,那么需要调用该 bean 对应的 FactoryBean 实例的 getObject() 方法,作为返回值。

无论是从缓存中获得到的 bean 还是通过不同的 scope 策略加载的 bean 都只是最原始的 bean 状态,并不一定就是我们最终想要的 bean。

举个例子,假如我们需要对工厂 bean 进行处理,那么这里得到的其实是工厂 bean 的初始状态,但是我们真正需要的是工厂 bean 中定义 factory-method 方法中返回的 bean,而 getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd) 方法,就是完成这个工作的。

参考:

《Spring 源码深度解析》- 郝佳

芋道源码

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、singleton
    • 1.1、加载前置处理
      • 1.2、获取Bean实例
        • 1.3、后置处理
          • 1.4、加入缓存
          • 二、Prototype
          • 三、其他作用域
          相关产品与服务
          容器服务
          腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档