Spring 系列之 Spring 源码笔记:bean 的加载-上【九】

经过前面的分析,完成了对 XML 配置文件的解析,接下来会面临更大的挑战,就是对 bean 加载的探索。bean 加载的功能实现远比 bean 的解析要复杂的多,同样,以 Spring 系列之 Spring 源码笔记:容器的基本实现-上【二】 中的示例为基础,对于加载 bean 的功能,在 Spring 中的调用方式为:

MyTestBean bean = (MyTestBean) beanFactory.getBean("myTestBean");

先看下实现代码:
在这里插入图片描述
仅从代码量上就能看出 bean 的加载经历了一个相当复杂的过程,其中涉及各种各样的考虑。对于加载过程中所涉及的步骤在代码中已经进行了详细地注释,其中最重要的是步骤8,针对不同 scope 进行 bean 的创建,你会看到各种常用的 Spring 特性在这里的实现。下面的图反映了整个过程:

AbstractBeanFactory TypeConverter doGetBean(String, Class<T>, Object[], boolean) 1 beanName = transformedBeanName(name) 2 sharedInstance = getSingleton(beanName) 3 检查缓存中对应的已经加载的bean bean = getObjectForBeanInstance(sharedInstance, name, beanName, null) 4 loop [sharedInstance != null && args == null] 返回对应的实例, 有时候存在诸如 BeanFactory 的情况 并不是直接返回实体本身而是返回指定方法返回的实例 如果当前(指缓存或ObjectFactory)不存在beanName, 调用方法 parentBeanFactory.getBean 去父类工厂中寻找,父类工厂通常为空 loop [parentBeanFactory != null && !containsBeanDefinition(beanName)] RootBeanDefinition mbd= getMergedLocalBeanDefinition(sharedInstance, name, beanName, null) 5 将存储 XML 配置文件的 GenericBeanDefinition 转换为 RootBeanDefinition, 如果指定 BeanName 是子 Bean 的话同时会合并父类的相关属性 mbd: RootBeanDefinition 6 递归调用,实例化依赖bean 7 loop [根据属性be- an 的属性(sing- leton、proto- type、scope- 进行实例化)- ] T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType) 8 loop [如果指定的类型不为空需要进行类型转换,否则直接强制转换] loop AbstractBeanFactory TypeConverter

1. FactoryBean 的使用

一般情况下,Spring 通过反射机制利用 bean 的 class 属性指定实现类来实例化 bean。在某些情况下,实例化 bean 过程比较复杂,如果按照传统的方式,则需要在 bean 标签中提供大量的配置信息,配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring 为此提供了一个 org.Springframework.bean.factory.FactoryBean 的工厂类接口,用户可以通过实现该接口定制实例化 bean 的逻辑。

FactoryBean 接口对于 Spring 框架来说占有重要的地位,Spring 自身就提供了 70多个 FactoryBean 的实现。它们隐藏了实例化一些复杂 bean 的细节,给上层应用带来了便利。从 Spring 3.0 开始,FactoryBean 开始支持泛型,即接口声明改为 FactoryBean<T> 的形式:
在这里插入图片描述
在该接口中定义了以下3个方法:

  1. T getObject():返回由 FactoryBean 创建的 bean 实例,如果 isSingleton 返回 true,则该实例会放到 Spring 容器中的单例缓存池中。
  2. boolean isSingleton():返回由 FactoryBean 创建的 bean 实例的作用域是 singleton 还是 prototype。
  3. Class<T> getObjectType():返回 FactoryBean 创建的 bean 类型。

当配置文件中 bean 标签的 class 属性配置的实现类是 FactoryBean 时,通过 getBean() 方法返回的不是 FactoryBean 本身,而是 FactoryBean#getObject() 方法所返回的对象,相当于 FactoryBean#getObject() 代理了 getBean() 方法。例如,如果使用传统方式配置下面 Car 的 bean 标签时, Car 的每个属性分别对应一个 property 元素标签。

public class Car {
	private int maxSpeed;
	private String brand;
	private double price;
	// get/set 方法
}

如果用 FactoryBean 的方式就会灵活一些,下面通过逗号分隔符的方式一次性地为 Car 的所有属性指定配置值:

public class CarFactoryBean implements FactoryBean<Car>{
	private String carInfo;
	
	public Car getObject() throws Exception{
		Car car = new Car();
		String[] infos = carInfo.split(",");
		car.setBrand(infos[0]);
		car.setMaxSpeed(Integer.valueOf(infos[1]));
		car.setPrice(Double.valueOf(infos[2]));
		return car;
	}
	
	public Class<Car> getObjectType(){
		return Car.class;
	}

	public boolean isSingleton(){
		return false;
	}

	public String getCarInfo(){
		return this.carInfo;
	}

	public String setCarInfo(String carInfo){
		return this.carInfo = carInfo;
	}
}

有了这个 CarFactoryBean 后,就可以在配置文件中使用下面这种自定义的配置方式配置 CarBean 了:

<bean id="car" class="xxx.CarFactoryBean" carInfo="超级跑车,400,200000">

当调用 getBean(“car”) 时,Spring 通过反射机制发现 CarFactoryBean 实现了 FactoryBean 的接口,这时 Spring 容器就调用接口方法 CarFactoryBean#getObject() 方法返回。如果希望获取 CarFactoryBean 的实例,则需要在使用 getBean(beanName) 方法时在 beanName 前显示的加上"&" 前缀,例如 getBean(“&car”)。

2. 缓存中获取单例 bean

前面已经提到过,单例在 Spring 的同一个容器内只会被创建一次,后续再获取 bean 直接从单例缓存中获取,当然这里也只是尝试加载,首先尝试从缓存中加载,然后再次尝试从 singletonFactories 中加载。因为在创建单例 bean 的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,Spring 创建 bean 的原则是不等 bean 创建完就会将创建 bean 的 ObjectFactory 提早曝光加入到缓存中,一旦下一个 bean 创建时需要依赖上个 bean,则直接使用 ObjectFactory
在这里插入图片描述
在这里插入图片描述
这个方法因为涉及循环依赖的检测,以及涉及很多变量的记录存取,所以初次看的话会很难理解。这里分析如下:

  1. 首先尝试从 singletonObjects 里面获取实例;
  2. 如果获取不到再从 earlySingletonObjects 里面获取;
  3. 如果还获取不到,再尝试从 singletonFactories 里面获取 beanName 对应的 ObjectFactory;
  4. 然后调用这个 ObjectFactory 的 getObject 方法来创建 bean,并放到 earlySingletonObjects 里面去,并且从 singletonFactories 里面移除这个 ObjectFactory;
  5. 对于后续的所有内存操作都只是为了循环依赖检测时使用,也就是在 allowEarlyReference 为 true 的情况下才会使用。

这里涉及用于存储 bean 的不同的 map,简单解释如下:

  • singletonObjects:用于保存 BeanName 和创建 bean 实例之间的关系,bean name–>bean instance;
  • singletonFactories:用于保存 BeanName 和创建 bean 的工厂之间的关系,bean name --> ObjectFactory;
  • earlySingletonObjects:也是保存 BeanName 和创建 bean 实例之间的关系,与 singletonObjects 的不同之处在于,当一个单例 bean 被放到这里面后,那么当 bean 还在创建过程中,就可以通过 getBean 方法获取到了,其目的是用来检测循环引用
  • registeredSingletons:用来保存当前所有已注册的 bean

3. 从 bean 的实例中获取对象

在 getBean 方法中,getObjectForBeanInstance 是个高频率使用的方法,无论从缓存中获得 bean 还是根据不同的 scope 策略加载的 bean。总之,我们得到 bean 的实例后要做的第一步就是调用这个方法来检测一下正确性,其实就是用于检测当前 bean 是否是 FactoryBean 类型的 bean,如果是,那么需要调用该 bean 对应的 FactoryBean 实例中的 getObject() 方法作为返回值。

无论从缓存中获得 bean 还是根据不同的 scope 策略加载的 bean 都只是最原始的 bean 状态,并不一定是我们最终想要的 bean。举个例子,假如我们需要对工厂 bean 进行处理,那么这里得到的其实是工厂 bean 的初始状态,但是我们真正需要的是工厂 bean 中定义的 factory-method 方法中返回的 bean,而 getObjectForBeanInstance 方法就是完成这个工作的:

	/**
	 * Get the object for the given bean instance, either the bean
	 * instance itself or its created object in case of a FactoryBean.
	 * 我们得到bean实例后第一步就是调用这个方法来检测以下正确性,
	 * ☆☆☆☆☆其实就是用于检测当前 bean 是否是 FactoryBean 的 bean, 如果是,那么需要调用该 bean 对应
	 * FactoryBean 实例中的 getObject 方法作为返回值
	 * 1.对 FactoryBean 正确性的验证
	 * 2.对非 FactoryBean 不做任何处理
	 * 3.对 bean 进行转换
	 * 4.将从 Factory 中解析 bean 的工作委托给 getObjectFromFactoryBean 方法
	 * @param beanInstance the shared bean instance
	 * @param name the name that may include factory dereference prefix
	 * @param beanName the canonical bean name
	 * @param mbd the merged bean definition
	 * @return the object to expose for the bean
	 */
	protected Object getObjectForBeanInstance(
			Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

		// Don't let calling code try to dereference the factory if the bean isn't a factory.
		// 如果指定的 name 是工厂相关(以&为前缀)
		if (BeanFactoryUtils.isFactoryDereference(name)) {
			if (beanInstance instanceof NullBean) {
				return beanInstance;
			}
			// beanInstance 不是 FactoryBean 类型, 验证不通过
			if (!(beanInstance instanceof FactoryBean)) {
				throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
			}
		}

		// Now we have the bean instance, which may be a normal bean or a FactoryBean.
		// If it's a FactoryBean, we use it to create a bean instance, unless the
		// caller actually wants a reference to the factory.
		// 现在我们有了个 bean 实例, 这个实例可能会是正常的 bean 或者是一个 FactoryBean.
		// 如果是一个 FactoryBean, 我们用它创建一个 bean 实例, 但是如果用户想要直接获取工厂实例
		// 而不是工厂的 getObject 方法对应的实例那么传入的 name 应该加上前缀&
		if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
			// 不是FactoryBean, 直接返回 beanInstance
			return beanInstance;
		}

		// 加载 FactoryBean
		Object object = null;
		if (mbd == null) {
			// 尝试从缓存中加载 bean
			object = getCachedObjectForFactoryBean(beanName);
		}
		if (object == null) {
			// Return bean instance from factory.
			// 到这里已经明确知道 beanInstance 一定是 FactoryBean 类型
			FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
			// Caches object obtained from FactoryBean if it is a singleton.
			// containsBeanDefinition 检测 beanDefinitionMap 中也就是在所有已经加载的类中检测是否定义了 beanName
			if (mbd == null && containsBeanDefinition(beanName)) {
				// 将存储 XML 配置文件的 GenericBeanDefinition 转换为 RootBeanDefinition,
				// 如果指定 BeanName 是子 Bean 的话同时会合并父类的相关属性
				mbd = getMergedLocalBeanDefinition(beanName);
			}
			// 是否是用户定义的而不是应用程序本身定义的
			boolean synthetic = (mbd != null && mbd.isSynthetic());
			// ********************
			object = getObjectFromFactoryBean(factory, beanName, !synthetic);
		}
		return object;
	}

从上面的代码来看,其实这个方法并没有什么重要的信息,大多是些辅助代码以及一些功能性的判断,而真正的核心代码却委托给了 getObjectFromFactoryBean,继续跟踪该方法:

/**
	 * Obtain an object to expose from the given FactoryBean.
	 * @param factory the FactoryBean instance
	 * @param beanName the name of the bean
	 * @param shouldPostProcess whether the bean is subject to post-processing
	 * @return the object obtained from the FactoryBean
	 * @throws BeanCreationException if FactoryBean object creation failed
	 * @see org.springframework.beans.factory.FactoryBean#getObject()
	 */
	protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
		// 如果是单例模式
		if (factory.isSingleton() && containsSingleton(beanName)) {
			synchronized (getSingletonMutex()) {
				Object object = this.factoryBeanObjectCache.get(beanName);
				if (object == null) {
					// *******************************
					object = doGetObjectFromFactoryBean(factory, beanName);
					// Only post-process and store if not put there already during getObject() call above
					// (e.g. because of circular reference processing triggered by custom getBean calls)
					Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
					if (alreadyThere != null) {
						object = alreadyThere;
					}
					else {
						if (shouldPostProcess) {
							if (isSingletonCurrentlyInCreation(beanName)) {
								// Temporarily return non-post-processed object, not storing it yet..
								return object;
							}
							beforeSingletonCreation(beanName);
							try {
								// ********** 调用 ObjectFactory 的后处理器 ***********
								object = postProcessObjectFromFactoryBean(object, beanName);
							}
							catch (Throwable ex) {
								throw new BeanCreationException(beanName,
										"Post-processing of FactoryBean's singleton object failed", ex);
							}
							finally {
								afterSingletonCreation(beanName);
							}
						}
						if (containsSingleton(beanName)) {
							this.factoryBeanObjectCache.put(beanName, object);
						}
					}
				}
				return object;
			}
		}
		else {
			// *****************************
			Object object = doGetObjectFromFactoryBean(factory, beanName);
			if (shouldPostProcess) {
				try {
					// ********** 调用 ObjectFactory 的后处理器 ***********
					object = postProcessObjectFromFactoryBean(object, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
				}
			}
			return object;
		}
	}

很遗憾,在这个方法中还是没有看到想看到的代码,在这个方法中只做了一件事,就是返回的 bean 如果是单例的,那就必须保证全局唯一,同时,也因为是单例的,所以不必重复创建,可以使用缓存来提高性能。

在 doGetObjectFromFactoryBean 方法中我们终于看到了我们想看到的方法,也就是 object = factory.getObject() 这句代码。
在这里插入图片描述
结合上面两个方法总结下,在调用 object = factory.getObject() 得到结果后并没有直接返回,而是接下来做了些后处理的操作,跟踪 AbstractAutowireCapableBeanFactory 类的 postProcessObjectFromFactoryBean 方法代码如下:
在这里插入图片描述
在这里插入图片描述
对于后处理器的使用后续会使用大量篇幅介绍,这里只需要了解在 Spring 获取 bean 的规则中有这么一条:尽可能保证所有 bean 初始化后都会调用注册的 BeanPostProcessor 的 postProcessAfterInitialzation 方法进行处理,在实际开发中可以针对此特性设计自己的业务逻辑。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值