在六、Spring 源码解析之 bean 的加载过程介绍中我们介绍了bean 的加载的大致流程,其中第一步已经在该文中说过,这里来说第二步:尝试从缓存中加载单例。
说道从缓存中加载单例,就不得不提到 FactoryBean 的使用:
一、FactoryBean 的使用
一般情况下,Spring 通过反射机制利用 bean 的 class 属性指定实现类来实例化 bean 。在某些情况下,实例化 bean 过程就比较复杂,如果按照传统的方式,则需要在 <bean>
中提供大量的配置信息,配置方式的灵活性是受限得到,这是采用编码的方式可能会得到一个简单打方案。Spring 为此提供了一个 org.springframework.beans.factory.FactoryBean
的工厂类接口,用户可以通过实现该接口定制实例化 bean 的逻辑。
public interface FactoryBean<T> {
public static final String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
/**
* 返回由 FactoryBean 创建的 bean 实例,如果是 isSingleton() 返回 true,则该实例会放到 Spring 容器中单实例缓存池中
*/
T getObject() throws Exception;
/**
* 返回由 FactoryBean 创建的 bean 类型
*/
Class<?> getObjectType();
/**
* 返回由 FactoryBean 创建的 bean 实例的作用域是 singleton 还是 prototype,默认是 singleton
*/
default boolean isSingleton() {
return true;
}
}
当配置文件中 <bean>
的 class 属性配置的实现类是 FactoryBean
时,通过 factory.getBean("testbean")
方法返回的不是 FactoryBean
本身,而是 FactoryBean#getObject()
方法返回的对象,相当于 FactoryBean#getObject()
代理了 getBean() 方法。例如:如果使用传统方式配置下面 User 的 <bean>
时, User 的每个属性分别对应一个 <property>
元素标签。
public class User {
private String userName;
private String email;
...
}
<bean id="user" class="org.pc.User">
<property name="userName" value="zs"/>
<property name="email" value="123@qq.com"/>
</bean>
如果使用 FactoryBean
的方式实现就会灵活一点,下面通过逗号分割符的方式一次性地为 User 的所有属性指定配置值:
import org.springframework.beans.factory.FactoryBean;
public class UserFactoryBean implements FactoryBean<User> {
private String userInfo;
public void setUserInfo(String userInfo) {
this.userInfo = userInfo;
}
public String getUserInfo() {
return userInfo;
}
public User getObject() throws Exception {
User user = new User();
String[] infos = this.userInfo.split(",");
user.setUserName(infos[0]);
user.setEmail(infos[1]);
return user;
}
public Class<?> getObjectType() {
return User.class;
}
public boolean isSingleton() {
return false;
}
}
<bean id="user" class="org.pc.User" userInfo="zs,123@qq.com"/>
当调用 factory.getBean("user")
时,Spring 通过反射机制发现 UserFactoryBean
实现了 FactoryBean
的接口,这时 Spring 容器就调用接口方法 UserFactoryBean#getObject()
方法返回。如果希望通过 factory.getBean("user")
获取 UserFactoryBean
实例,则需要在 beanName
加上前缀 “&”,例如:factory.getBean("&user")
二、尝试从缓存中获取单例 bean
介绍过 FactoryBean
的用法后,我们就可以了解 bean 的加载过程了。前面已经提到过,单例在 Spring 的同一个容器内只会被创建一次,后续再获取 bean 直接从单例缓存中虎丘,当然这里也只是尝试加载,首先尝试从缓存中加载,然后再次尝试从 sin个singletonFactories
中加载。因为在创建 bean 的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,Spring 创建 bean 的原则是不等 bean 创建完成就会将创建 bean 的 ObjectFactory
提早曝光加入到缓存中,一旦下一个 bean 创建时需要依赖上个 bean,则直接使用 ObjectFactory
。
-
AbstractBeanFactory#doGetBean(final String name, @Nullable final Class requiredType,@Nullable final Object[] args, boolean typeCheckOnly)
-
DefaultSingletonBeanRegistry#getSingleton(String beanName)
-
DefaultSingletonBeanRegistry#getSingleton(String beanName, boolean allowEarlyReference)
/**
* DefaultSingletonBeanRegistry 类下的方法
*/
public Object getSingleton(String beanName) {
// 参数 true 设置标识容许早期依赖
return getSingleton(beanName, true);
}
/**
* DefaultSingletonBeanRegistry 类下的方法
*/
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//检查缓存中是否存在实例
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 如果为空 且 单例正在加载过程中,则锁定全局变量
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
// allowEarlyReference 为 true,则表明需要循环依赖检测
if (singletonObject == null && allowEarlyReference) {
/*
* 当某些方法需要提前初始化的时候则会调用 addSingletonFactory() 方法将对应的 ObjectFactory
* 初始化策略存储在 singletonFactories
*
* 补充说明:
* 在创建 bean 的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,Spring 创建 bean
* 的原则是不等 bean 创建完成就会将创建 bean 的 ObjectFactory 提早曝光加入到缓存中,一旦下一个 bean
* 创建时需要依赖上个 bean,则直接使用 ObjectFactory
*/
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
// 调用预先设定的 getObject() 方法
singletonObject = singletonFactory.getObject();
// 记录在缓存中,earlySingletonObjects 和 singletonFactories 互斥
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
这个方法因为涉及循环依赖的检测,以及设计很多变量的记录存取,所以很容易让人摸不着头脑。这个方法首先尝试从 singletonObjects
里面获取实例,如果获取不到再从 earlySingletonObjects
里面获取,如果还是获取不到,再尝试从 singletonFactories
里面获取 beanName
对应的 ObjectFactory
,然后调用这个 ObjectFactory
的 getObject()
方法来创建 bean,并放到 earlySingletonObjects
里面去,并且从 singletonFactories
里面 remove 掉这个 ObjectFactory
,而对于后续的所有内存操作都只是为了循环依赖检测时使用,也就是在 allowEarlyReference
为 true 的情况下才会使用。
这里涉及到用于存储 bean 的不同 map,简单解释如下:
- singletonObjects:用于保存 beanName 和创建 bean 实例之间的关系,bean name -> bean instance
- singletonFactories:用于保存 beanName 和创建 bean 的工厂之间的关系,bean name -> bean ObjectFactory
- earlySingletonObjects:用于保存 beanName 和创建 bean 实例之间的关系,与
singletonObjects
的不同之处在于,当一个单例 bean 被放到这里面后,那么当 bean 还在创建过程中,就可以通过 getBean() 方法获取到了,其目的是用来检测循环引用。 - registeredSingletons:用来保存当前所有已经注册的 bean