Bean loading of Spring Ioc (3): Bean creation of each scope

In Spring, beans have many different scopes, such as: singleton, prototype, request, etc. This article will analyze how the beans of each scope are created

一、singleton

Code:

// 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);
	}
  • An anonymous inner class is used here, and createBean(beanName, mbd, args)an ObjectFactory is obtained through the method first.

  • Pass the ObjectFactory as a parameter to the getSingleton(beanName,objectFactory)method

  • Use the sharedInstance returned by the getSingleton (beanName, objectFactory) method as a parameter getObjectForBeanInstance(sharedInstance, name, beanName, mbd)to pass in the final Bean instance (see Spring Ioc Bean Loading (1) for details )

createBean(beanName, mbd, args)The method is more complicated, and will be analyzed in detail in the following articles. I will skip it here and see the getSingleton(beanName,objectFactory)method .

// 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;
	}
	}

In this code, in fact, some preparation and preprocessing steps are mainly done. The real creation of Bean is singletonFactory.getObject()implemented in the method, and the singletonFactory is a parameter that is called back by the createBean() method.
So what does this code mainly do?

  • Try to get the singleton bean from the cache and
    return directly if it has been loaded, otherwise start the loading process

  • Load preprocessing

  • Get Bean instance

  • post processing

  • add cache

1.1. Loading preprocessing

beforeSingletonCreation(beanName)is a marker method, let's look at the code:

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

Add the beanName to the singletonsCurrentlyInCreationmap to indicate that the singleton bean is being created, and throw an exception if the addition fails.

1.2, get Bean instance

Get the Bean through the singletonFactorycreateBean(beanName) returned by the method .

1.3. Post-processing

afterSingletonCreation(beanName)It is also a representation method:

// 用于移除标记,当前 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");
		}
	}

Remove the create flag after creating the bean.
This creation flag of preprocessing and postprocessing will be isSingletonCurrentlyInCreation(String beanName)used when calling, and this method is used to determine whether the current bean is already being created.

1.4, add cache

Look directly at the code:

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);
	    }
	}

One put, one add, two remove operations.

  • [put] singletonObjects attribute, the cache of singleton bean.

  • [remove] singletonFactories property, the cache of singleton bean Factory.

  • [remove] earlySingletonObjects property, the cache of singleton beans created "early".

  • [add] registeredSingletons attribute, the registered singleton cache.

二、Prototype

Code:

        //创建多例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);
	}

The prototype mode is very simple, just create a new instance directly, no longer get it from the cache.
beforePrototypeCreation(beanName)Preprocessing to mark the current bean as the prototype being created.
afterPrototypeCreation(beanName)Post-processing, cancel the creation of the current bean flag.
Call getObjectFrBeanInstance()the method to get the final bean. (For details, see Spring Ioc Bean Loading (1) details )

3. Other scopes

//要创建的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);
		}
	}

Divided into the following steps:

  • Get scope name from Scope annotation

  • preprocessing

  • createBean()

  • post processing

  • scope.get() gets the bean

  • getObjectForBeanInstance() method to get Bean

The core process is the same as the prototype mode, except that scope.get() is called here to get the bean.

Object get(String name, ObjectFactory<?> objectFactory);

scope.get() is an interface that has various implementation classes:

Let's take a look at an implementation of SimpleThreadScope that comes with spring:

//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;
	}

The implementation of other scopes will not be seen one by one, and interested friends can take a look for themselves.

总结

There are 2 important methods in the above code:

  • createBean (beanName, mbd, args)

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

These two methods are used in the three code branches. The createBeannext article will analyze them in detail. The getObjectForBeanInstancemethods have been analyzed in Spring Ioc Bean Loading (1) .
Here is another reference to the analysis of this method in "Spring Source Code Depth Analysis":

This method is mainly to verify the correctness of the bean we get below, in fact, it is to detect whether the current bean is a bean of type FactoryBean.
If it is, then you need to call the getObject() method of the FactoryBean instance corresponding to the bean as the return value.
Whether it is a bean obtained from a cache or a bean loaded through a different scope strategy, it is only the most primitive bean state, not necessarily the bean we want in the end.
For example, if we need to process the factory bean, what we get here is actually the initial state of the factory bean, but what we really need is the bean returned in the factory-method method defined in the factory bean, and getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd) method is to complete this work.

Reference:
"In-depth Analysis of Spring Source Code" - Hao Jia
Taro Source Code

{{o.name}}
{{m.name}}

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=324079129&siteId=291194637