共计 11428 个字符,预计需要花费 29 分钟才能阅读完成。
之前松哥写过一篇文章,跟小伙伴们介绍了咱们在面试中十分常见的一道面试题:
- Spring 中 BeanFactory 和 FactoryBean 有何区别?
在这篇文章中,松哥也和各位小伙伴演示了了 FactoryBean 的一些具体用法,然而对于 FactoryBean 的一些具体实际,这篇文章中没有讲,那么明天我就来和大家聊一聊这个话题,顺便再来说说 FactoryBean 的兄弟 SmartFactoryBean。
1. 应用差异
FactoryBean 的用法我就不再反复了,这里来看下 SmartFactoryBean。
FactoryBean 有很多实现类,然而继承自 FactoryBean 的接口却只有 SmartFactoryBean 一个。
SmartFactoryBean 接口的定义如下:
public interface SmartFactoryBean<T> extends FactoryBean<T> {default boolean isPrototype() {return false;}
default boolean isEagerInit() {return false;}
}
能够看到,SmartFactoryBean 就是在 FactoryBean 的根底之上多了两个办法:
- isPrototype:这个办法就是返回以后 Bean 是否是多实例。初看这个办法,有的小伙伴可能会感觉到惊讶,因为在 FactoryBean 中实际上有一个跟它性能相似的办法叫做 isSingleton,isSingleton 的意思就是说这个 Bean 是否是单例的,那么为什么当初还多了一个 isPrototype 办法呢?在后面的视频中松哥和大家讲过,Spring 中 Bean 的 scope 一共有六种,singleton 和 prototype 只是其中的两种,所以,isSingleton 为 true 就示意是单例,然而为 false 并不能示意就是 prototype,同理,isPrototype 为 true 就示意是多实例,然而 isPrototype 为 false 并不能示意就是 singleton,因而,这两个办法是不抵触的。
- isEagerInit:这个办法就好了解了,办法名示意是否要提前初始化 Bean。当咱们应用 FactoryBean 的时候,默认状况下,Spring 在初始化 Bean 的时候,初始化的是工厂 Bean,例如咱们有一个 UserFactoryBean,那么默认状况下,Spring 容器初始化的是 UserFactoryBean,而 UserFactoryBean 中 getObject 办法真正要返回的 User 则在第一次应用的时候,才会被初始化,人不知; 鬼不觉中,指标 Bean 的初始化就被提早了。如果不应用 SmartFactoryBean 的话,那咱们得通过 Bean 的提前注入等形式去实现 Bean 的提前初始化,如果应用 SmartFactoryBean 的话,那么就能够通过配置 isEagerInit 办法返回 true 来实现目标 Bean 提前初始化了。
对于第二个办法 isEagerInit,我举个例子给大家演示一下。
假如我有一个 User 类,如下:
public class User {public User() {System.out.println("User-init");
}
}
而后又有一个 UserFactoryBean,如下:
@Component
public class UserFactoryBean implements FactoryBean<User> {public UserFactoryBean() {System.out.println("UserFactoryBean-init");
}
@Override
public User getObject() throws Exception {return new User();
}
@Override
public Class<?> getObjectType() {return User.class;}
}
最初扫描这个 Bean 并且启动容器:
@Configuration
@ComponentScan
public class JavaConfig {
}
public class Demo {public static void main(String[] args) {AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
}
}
那么当我只初始化容器,不从容器中获取任何 Bean,控制台就会只打印 UserFactoryBean-init
。
这就阐明只有 UserFactoryBean 被 Spring 容器初始化了,咱们的 User 对象其实还没被初始化,User 对象要在第一次应用的时候,才会被初始化。
如果咱们的 UserFactoryBean 实现的是 SmartFactoryBean 接口,那么就能够依照如下形式进行配置:
@Component
public class UserFactoryBean implements SmartFactoryBean<User> {public UserFactoryBean() {System.out.println("UserFactoryBean-init");
}
@Override
public boolean isEagerInit() {return true;}
@Override
public User getObject() throws Exception {return new User();
}
@Override
public Class<?> getObjectType() {return User.class;}
}
此时咱们启动容器,然而却不获取任何 Bean,那么大家就会发现,User-init
也打印进去了,指标 Bean 也被初始化了。
2. 原理剖析
接下来咱们就从源码的角度来和大家简略梳理一下。
大家晓得,容器的初始化是从 refresh 办法开始的,refresh 在初始化的过程中会调用到 finishBeanFactoryInitialization,而在 finishBeanFactoryInitialization 办法中则会调用到 beanFactory.preInstantiateSingletons() 办法,这个办法的作用就是去初始化那些不是提早加载的 Bean。
所以,问题的外围就在 beanFactory.preInstantiateSingletons() 办法中,一起来看下。
DefaultListableBeanFactory#preInstantiateSingletons:
@Override
public void preInstantiateSingletons() throws BeansException {List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
for (String beanName : beanNames) {RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {if (isFactoryBean(beanName)) {Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof SmartFactoryBean<?> smartFactoryBean && smartFactoryBean.isEagerInit()) {getBean(beanName);
}
}
else {getBean(beanName);
}
}
}
for (String beanName : beanNames) {Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton smartSingleton) {StartupStep smartInitialize = this.getApplicationStartup().start("spring.beans.smart-initialize")
.tag("beanName", beanName);
smartSingleton.afterSingletonsInstantiated();
smartInitialize.end();}
}
}
这个办法的逻辑还是比拟好了解。
首先 beanNames 中保留的就是所有 bean 名称,而后进行遍历。
遍历的时候依据 beanName 获取到 RootBeanDefinition,而后查看类是否不是抽象类、是否是单例以及是否不提早初始化,如果满足条件,那么就开始初始化。
初始化的时候,首先判断以后 beanName 是否是一个 FactoryBean,大家留神,如果是 FactoryBean,则调用 getBean 办法去获取 Bean,然而调用的时候,在 beanName 的后面加上了 FACTORY_BEAN_PREFIX,这个其实就是 &
,在后面的视频中松哥和大家讲过,加上 &
之后,这里获取到的就不是指标 Bean,而是这个 FactoryBean。这就是为什么在第一大节中和大家说,应用 FactoryBean 会导致指标 Bean 提早加载,起因就在这里,因为初始化的时候给 beanName 加上了 &
前缀,所以初始化的就不是指标 Bean 了。
接下来还有一个判断,如果初始化进去的 Bean 是一个 SmartFactoryBean 对象,并且 isEagerInit 办法还返回 true,那么就再次调用 getBean 办法进行 Bean 的初始化,此时 Bean 的初始化传入的 beanName 就没有增加前缀了,那么初始化的就是指标 Bean 了,这也和咱们第一大节中讲的论断相符。
初始化的时候,如果判断以后 bean 不是一个 FactoryBean,那么就间接调用 getBean 办法进行 Bean 的初始化。
最初还有一段逻辑,就是依据 beanName 获取到实例名称,如果这个实例是一个 SmartInitializingSingleton 类型的,那么就调用一下它的 afterSingletonsInstantiated 办法。
那么下面这段源码还波及到两个中央,别离是 isFactoryBean 和 getBean。
2.1 isFactoryBean
这个办法就是依据 beanName 判断是否是一个 FactoryBean,如下:
@Override
public boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException {String beanName = transformedBeanName(name);
Object beanInstance = getSingleton(beanName, false);
if (beanInstance != null) {return (beanInstance instanceof FactoryBean);
}
// No singleton instance found -> check bean definition.
if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory cbf) {
// No bean definition found in this factory -> delegate to parent.
return cbf.isFactoryBean(name);
}
return isFactoryBean(beanName, getMergedLocalBeanDefinition(beanName));
}
protected boolean isFactoryBean(String beanName, RootBeanDefinition mbd) {
Boolean result = mbd.isFactoryBean;
if (result == null) {Class<?> beanType = predictBeanType(beanName, mbd, FactoryBean.class);
result = (beanType != null && FactoryBean.class.isAssignableFrom(beanType));
mbd.isFactoryBean = result;
}
return result;
}
这个里边,首先调用 transformedBeanName 办法对 beanName 进行一个预处理:
protected String transformedBeanName(String name) {return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
public static String transformedBeanName(String name) {if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {return name;}
return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
do {beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
return beanName;
});
}
public String canonicalName(String name) {
String canonicalName = name;
// Handle aliasing...
String resolvedName;
do {resolvedName = this.aliasMap.get(canonicalName);
if (resolvedName != null) {canonicalName = resolvedName;}
}
while (resolvedName != null);
return canonicalName;
}
大家看到,在动态的 transformedBeanName 办法中,首先判断 beanName 是否是以 &
结尾,如果不是,则间接返回 beanName 即可。否则就通过一个 do{}while() 将 beanName 中的 &
都给删除掉,防止出现相似 &&&&&&&user
这种 beanName。
而后调用 canonicalName 办法获取到标准的 beanName,因为 bean 可能存在别名,如果应用的是别名,则将之在 canonicalName 办法中解析为标准的 beanName。
有了 beanName 之后,接下来调用 getSingleton 办法去一级缓存中查问这个 Bean 是否曾经实现初始化了,如果曾经实现,那么直接判断该 beanInstance 是否为 FactoryBean 即可,默认状况下,显然不会走这条线。
接下来持续判断以后 beanFactory 中是否存在该 beanName 的定义,如果不存在,且以后 beanFactory 是 ConfigurableBeanFactory,那么就去父容器中查看这个 beanName 对应的 bean 是否是 isFactoryBean。
最初切实不行,就调用另外一个重载的 isFactoryBean 办法去判断,这个重载的办法逻辑就比较简单了,从 BeanDefinition 中获取到 Bean 的类型,而后判断是否是 FactoryBean 即可。
这就是判断一个 beanName 对应的 Bean 是否为 FactoryBean 的所有逻辑。
2.2 getBean
另一方面就是 getBean 办法了,这个办法的执行能够分为两步。
再来回顾下 preInstantiateSingletons 办法中的如下逻辑:
if (isFactoryBean(beanName)) {Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof SmartFactoryBean<?> smartFactoryBean && smartFactoryBean.isEagerInit()) {getBean(beanName);
}
}
能够看到,无论是否提前初始化指标 Bean,都须要先初始化 FactoryBean,也就是主动加上 &
前缀而后去调用 getBean 办法,FactoryBean 的初始化就和一般 Bean 的初始化流程一样,我这里就不反复了。
而后,如果是要提前初始化 Bean,则还会再调用一次 getBean 办法,这次调用不加 &
前缀,所以这次调用最终就会触发到 FactoryBean 的 getObject 办法。
getBean 的调用最终会来到 AbstractBeanFactory#doGetBean 办法中,咱们来简略看下这个办法的逻辑,这个办法比拟长,我这里列出来跟咱们相干的一部分:
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {String beanName = transformedBeanName(name);
Object beanInstance;
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
// 省略。。。}
小伙伴们看到,这里首先会调用 transformedBeanName 办法对 bean 名称进行解决,解决之后的 bean 名称就不带 &
了(2.1 大节曾经介绍过),而后依据 bean 名称去单例池中获取 Bean 实例,如果是第一次来,也就是初始化 UserFactoryBean 的那一次,那次显然单例池中是没有货色的,那么那么就会进入到 Bean 的创立流程中,并在创立实现后,将 Bean 实例存入到单例池中(实际上存的是 UserFactoryBean 的实例)。
如果是第二次进来,因为上一次曾经实现了 UserFactoryBean 的初始化了,第二次进来单例池中显然是有货色的,而且这个货色就是 UserFactoryBean 的实例,所以第二次进来之后,会进入到接下来的 if 分支中(第一次不会进入到该分支),在这个分支中,最终触发 getObject 办法的调用。
大家留神,getObjectForBeanInstance 办法传入了两个 bean 名称参数,第一个 name 是没有去除
&
的 beanName(可能蕴含&
前缀),第二个参数则是通过解决的 beanName,即去除了&
的 beanName。
来看下 getObjectForBeanInstance 办法:
protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {if (BeanFactoryUtils.isFactoryDereference(name)) {if (beanInstance instanceof NullBean) {return beanInstance;}
if (!(beanInstance instanceof FactoryBean)) {throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
}
if (mbd != null) {mbd.isFactoryBean = true;}
return beanInstance;
}
if (!(beanInstance instanceof FactoryBean)) {return beanInstance;}
Object object = null;
if (mbd != null) {mbd.isFactoryBean = true;}
else {object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
if (mbd == null && containsBeanDefinition(beanName)) {mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
return object;
}
整体上来看,这里有四个 if 分支,咱们别离来看。
第一个 if 分支次要是判断想要获取的 Bean 到底是不是一个 FactoryBean?BeanFactoryUtils.isFactoryDereference(name) 其实就是判断以后这个 name 是否以 &
开始,如果是以 &
开始,那就阐明想要获取的就是 FactoryBean 实例,此时就查看 beanInstance 是否为 NullBean,是否为 FactoryBean,如果都检测没问题,那么就把 bean 间接返回即可。如果咱们是想要从 Spring 容器中获取一个 FactoryBean 的实例,那么很显著就是走的这条线。
第二个 if 是查看 beanInstance 如果不是 FactoryBean 的实例,阐明可能就是一个一般 Bean,那么就不须要额定解决,间接返回即可。
第三个 if 是标记 FactoryBean 的,这个没啥好说的。
第四个 if 分支则是将 beanInstance 转为 FactoryBean,而后合并一下 BeanDefinition,进而判断一下这个 Bean 是否是在外部应用(synthetic),最初调用 getObjectFromFactoryBean 办法去获取 Bean 对象,留神第三个参数是 !synthetic
,这个参数示意这个类是否要应用 BeanPostProcessor 对其进行解决,只有这个 Bean 不是外部应用(synthetic=false),那么就会给其利用上 BeanPostProcessor。
持续来看 getObjectFromFactoryBean 办法:
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);
Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
if (alreadyThere != null) {object = alreadyThere;}
else {if (shouldPostProcess) {if (isSingletonCurrentlyInCreation(beanName)) {return object;}
beforeSingletonCreation(beanName);
object = postProcessObjectFromFactoryBean(object, beanName);
}
if (containsSingleton(beanName)) {this.factoryBeanObjectCache.put(beanName, object);
}
}
}
return object;
}
}
else {Object object = doGetObjectFromFactoryBean(factory, beanName);
if (shouldPostProcess) {object = postProcessObjectFromFactoryBean(object, beanName);
}
return object;
}
}
这里首先调用 factory.isSingleton() 办法去判断这个 Bean 是否是单例模式,该办法就是咱们第一大节和大家剖析的办法。
如果是单例模式,则去单例池 factoryBeanObjectCache 中获取到 Bean 并返回即可,当然,单例池中可能并不存在这个 Bean,那么就调用 doGetObjectFromFactoryBean 办法进行加载,加载胜利之后,再给其利用上 BeanPostProcessor,最初还要将加载的后果存入到单例池 factoryBeanObjectCache 中,不便下一次应用。
如果不是单例模式,那么就不去单例池中查找,间接调用 doGetObjectFromFactoryBean 办法去获取 Bean 实例即可,获取到之后,也依据 shouldPostProcess 参数为之利用 BeanPostProcessor。
所有的线索都指向了 doGetObjectFromFactoryBean,咱们再来看这个办法:
private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException {
Object object;
if (System.getSecurityManager() != null) {AccessControlContext acc = getAccessControlContext();
object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
}
else {object = factory.getObject();
}
}
if (object == null) {object = new NullBean();
}
return object;
}
这里做了一些权限的判断(避免对代码块没有执行权限),当然无论是否有权限,最终都会调用到 factory.getObject() 办法,终于到起点啦~
拿到 object 之后,再做一个简略判断,如果 object 为 null,那么就创立一个 NullBean 并返回即可。
好啦,这就是 FactoryBean 的残缺创立流程啦~
3. 小结
好啦,明天就和小伙伴们分享了 FactoryBean 和它的兄弟 SmartFactoryBean,其实无论是指标 Bean 还是 FactoryBean,一开始的解决流程都是类似的,一致产生在 AbstractBeanFactory#doGetBean 办法中,从这个办法中是否获取到 beanInstance 实例开始,一个向东一个向西~
小伙伴们无妨 debug 走一遍流程哦~