Spring 之 FactoryBean 简介与源码解析
Spring 之 FactoryBean 简介与源码解析
1、简介
Spring 中有两种类型的Bean,一种是普通Bean,另一种是工厂Bean 即 FactoryBean。
实现了FactoryBean接口的类是一个特殊的Bean。通过实现这个接口,我们可以自定义Bean的创建过程,灵活地控制Bean的实例化和配置。FactoryBean的实现类是一个工厂,它负责产生
其他Bean的实例。当我们在Spring配置文件中定义一个FactoryBean时,实际上创建的是这个工厂Bean本身。当需要使用这个Bean时,Spring容器会调用FactoryBean的getObject()方法来
获取由工厂Bean产生的实例对象。相比于普通的Bean,FactoryBean更加灵活和强大。我们可以在getObject()方法中编写自定义的逻辑来决定实例化哪个对象,并可以对其进行进一步的配
置和处理(比如通过反射动态注册Bean到IOC容器中)。如果要获取FactoryBean对象,请在BeanName前面加一个 & 符号来获取(一般没必要获取FactoryBean对象)。创建出来的对象
是否属于单例由isSingleton中的返回决定。
2、关于BeanFactory
在Spring中,BeanFactory是负责创建和管理Bean实例的核心容器。它是Spring框架的基础,提供了依赖注入和控制反转等重要特性。BeanFactory负责根据配置信息创建和维护Bean
对象的生命周期。 而 FactoryBean 只是 BeanFactory 容器中的一个特殊的Bean。
3、FactoryBean 的使用场景
FactoryBean 主要用于创建一些复杂的Bean, 比如运行时动态地创建一些Bean。 在一些三方框架(Mybatis 的Mapper接口、OpenFeign 的 FeignClient接口等)整合Spring时都会用到该接口。
4、FactoryBean 源码分析
// 工厂Bean,用于产生其他对象
public interface FactoryBean<T> {
// 获取 产生的真正的Bean实例
T getObject() throws Exception;
// 获取Bean工厂创建的对象的类型
Class<?> getObjectType();
// Bean工厂创建的对象是否是单例模式
boolean isSingleton();
}
我们之前讲bean的生命周期的时候,有讲到单例的bean都是在Spring容器启动的时候就初始化的,那么对于FactoryBean实例,它的FactoryBean#getObject方法也会在Spring容器启动的时候就初始化嘛? 带着这些疑问,我们来看一下获取bean的核心逻辑。
4.1、AbstractBeanFactory#doGetBean方法:
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) {
// 转换一下需要获取的beanName, 如果BeanName前面有 & 符号则去掉
final String beanName = transformedBeanName(name);
Object bean;
// 直接从一级缓存获取单例对象
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
// 获取最终返回的bean实例,FactoryBean的主要处理逻辑在这里
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {
// skip 省略其他代码...
if (mbd.isSingleton()) {
// spring容器启动的时候会走到这个分支
// 触发当前bean的初始化流程
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
// 初始化单例bean之后,拿到这个bean对象,最终也会调用这个方法
// 获取最终返回的bean实例,FactoryBean的主要处理逻辑在这里
// 需要注意的是,bean实例初始化之后调用的时候,多传了一个BeanDefinition参数
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
// 多例的bean
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
// 多例的时候也会调用
// 需要注意的是,bean实例初始化之后调用的时候,多传了一个BeanDefinition参数
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}else {
// 其他自定义scope
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException(..);
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
});
// 最后也是需要调用这个方法的
// 需要注意的是,bean实例初始化之后调用的时候,多传了一个BeanDefinition参数
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(...);
}
}
}
}
}
4.2、org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance() 获取最终需要返回的bean实例
/**
* 需要注意的是,这里传入了name和beanName两个值。name是transformedBeanName之前的原始值,也就是我们调用getBean方法时传入的,
* beanName就是转换后的啦,正常情况下(name没有前置的&标记),这两是一样的
* 如果mbd不为空,说明bean对象刚刚初始化完
*/
protected Object getObjectForBeanInstance(Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {
if (BeanFactoryUtils.isFactoryDereference(name)) {
// 如果name是带有&前缀的,说明我们是想获取factoryBean实例
// 而不是获取factoryBean#getObject返回的实例
if (beanInstance instanceof NullBean) {
return beanInstance;
}
// 判断一下,如果你通过&xxx来获取bean实例,那你获取到的bean实例必须实现FactoryBean接口
// 这种判断主要是杜绝意料之外的事情发生,比较beanName是用户指定的
// 要是用户指定一个bean名称是&xxx但是实际上是不实现FactoryBean是不允许的
// 启动就会报错
if (!(beanInstance instanceof FactoryBean)) {
throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
}
if (mbd != null) {
mbd.isFactoryBean = true;
}
// 这里就直接把factoryBean实例返回出去了
// 这就是我们getBean("&factoryBean")获取到factoryBean实例的原因
return beanInstance;
}
// 能走到这里,其实说明name是一个正常的非&开头的name了
if (!(beanInstance instanceof FactoryBean)) {
// 这个时候,如果获取到的bean实例没有实现FactoryBean接口,是不需要特殊处理的,直接返回就行了
// 对于正常的bean(没实现FactoryBean的),都是往这里返回的
return beanInstance;
}
Object object = null;
if (mbd != null) {
// 如果mbd不为空,说明bean对象(FactoryBean)刚刚初始化完
mbd.isFactoryBean = true;
} else {
// 不是bean对象(FactoryBean)刚刚初始化完,直接从缓存获取
object = getCachedObjectForFactoryBean(beanName);
}
if (object == null) {
// 如果缓存中没有这个factoryBean对应的subBean 或者是factoryBean刚初始化完的时候
FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
if (mbd == null && containsBeanDefinition(beanName)) {
mbd = getMergedLocalBeanDefinition(beanName);
}
boolean synthetic = (mbd != null && mbd.isSynthetic());
// 从factoryBean获取subBean并且返回
object = getObjectFromFactoryBean(factory, beanName, !synthetic);
}
// 这里返回了subBean
return object;
}
可以看到,如果a实例是一个factoryBean的话,当我们调用getBean("a")时,是会创建a实例并触发它的factoryBean#getObject获取到subBean实例并返回的; 而如果是使用getBean("&a"),则只会实例化a实例并返回factoryBean本身。
4.3、getObjectFromFactoryBean()从factoryBean获取subBean
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
// 注意这个isSingleton是FactoryBean#isSingleton
if (factory.isSingleton() && containsSingleton(beanName)) {
synchronized (getSingletonMutex()) {
// 加锁, 先从缓存拿一次
Object object = this.factoryBeanObjectCache.get(beanName);
if (object == null) {
// 确保缓存没有,才创建一个。这里就是简单的调用FactoryBean#getObject了,就不往下跟了
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) {
// 再从缓存中获取一遍,如果缓存中存在对象了,则把当前对象覆盖
// 并且会跳过subBean的beanPostProcessor调用流程,这里其实是用来解决循环依赖问题的
object = alreadyThere;
} else {
// 正常流程是走这里,到这里我们已经拿到subBean实例了
if (shouldPostProcess) {
// 如果当前subBean已经在创建中了,那就直接返回了。
// 其实就是判断在不在singletonsCurrentlyInCreation这个容器里
if (isSingletonCurrentlyInCreation(beanName)) {
return object;
}
// 把当前beanName加入singletonsCurrentlyInCreation容器(set)
// 如果加入不进去会报循环依赖错误
beforeSingletonCreation(beanName);
try {
// 调用beanPostProcessor,由于subBean的初始化/销毁等生命周期
// 都是由factoryBean自行管理的,所以这里就是调用了bean完全实例化之后的
// postProcessAfterInitialization方法, AOP切面就是在这个埋点里做的
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(...);
}
finally {
// 从singletonsCurrentlyInCreation容器删除
afterSingletonCreation(beanName);
}
}
if (containsSingleton(beanName)) {
// 最后放入缓存
this.factoryBeanObjectCache.put(beanName, object);
}
}
}
return object;
}
}
else {
// 非单例就直接从FactoryBean#getObject创建一个了
Object object = doGetObjectFromFactoryBean(factory, beanName);
if (shouldPostProcess) {
try {
// 调用BeanPostProcessor
object = postProcessObjectFromFactoryBean(object, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(...);
}
}
// 返回
return object;
}
}
5、FactoryBean 中 getObject() 方法 返回的 subBean的初始化时机
我们已经了解了getBean中对factoryBean的处理逻辑,简单的来讲,其实就是针对传入的name是否有&前缀,来走不同的分支逻辑。 那么现在又有一个问题了,单例的subBean对象, 到底是在什么时候创建并且被spring管理起来的呢? 我们知道,如果subBean在缓存factoryBeanObjectCache中没有对应的subBean,那么直接调用getBean("")肯定是会创建一个subBean的, 现在我想说的是,我们普通的单例bean会在spring容器启动的时候就初始化,单例的subBean也会在这个时候初始化么?要清楚这个问题,我们还是要直接去看源码,这个时候就需要看spring容器启动时,初始化所有单例bean的逻辑了:
// DefaultListableBeanFactory类中
public void preInstantiateSingletons() throws BeansException {
// 获取所有的beanName
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)) {
// 这里主要是通过beanDefinition中的信息,判断一下是否是factoryBean
// 如果是factoryBean,将会在beanName前面加上一个&符合再调用getBean
// 也就是说这个getBean是不会初始化subBean实例的
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
// 拿到bean的实例之后,就可以通过bean实例使用instanceof进行二次确认了
final FactoryBean<?> factory = (FactoryBean<?>) bean;
// 可以看到这里出现了一个SmartFactoryBean接口,且有一个isEagerInit方法
// 如果isEagerInit方法返回true,spring就认为这个subBean是需要提前初始化
boolean isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
if (isEagerInit) {
// 这个时候使用原始的beanName再调用一次getBean
// 这里就会触发subBean的初始化流程了
getBean(beanName);
}
}
}
else {
// 普通的bean直接走这里
getBean(beanName);
}
}
}
// 跳过
}
可以看到,对于我们的普通的subBean,在spring容器启动的时候,是不会主动去初始化的,而只会初始化factoryBean对象。 除非我们的factoryBean实现了FactoryBean的子接口SmartFactoryBean并表明该subBean需要提前初始化。 也简单看一下SmartFactoryBean接口的定义:
public interface SmartFactoryBean<T> extends FactoryBean<T> {
// 跟FactoryBean#isSingleton()差不多,但是用处稍微有点不一样
default boolean isPrototype() {
return false;
}
// 这个方法表明是否需要提前初始化
default boolean isEagerInit() {
return false;
}
}
6、总结:
- 单例的factoryBean对象本身会在spring容器启动时主动初始化。而subBean的初始化则是在第一次需要获取时才会触发。
- 如果factoryBean对象实现的接口是SmartFactoryBean且isEagerInit方法返回true,那么subBean对象也会在spring容器启动的时候主动初始化。
- 如果bean注册的时候,beanName对应的bean实例是一个factoryBean,那么我们通过getBean(beanName)获取到的对象将会是subBean对象;如果要获取工厂对象factoryBean,需要使用getBean("&" + beanName)。
- 单例的subBean也会缓存在spring容器中,具体的容器是FactoryBeanRegistrySupport#factoryBeanObjectCache,一个Map<beanName, subBean实例>。
- Spring的三级缓存设计解决了大部分循环依赖问题,而对与subBean与普通bean的循环依赖导致可能出现两个subBean对象的问题,spring采用多重检查的方式,丢弃掉其中一个无用的subBean,保留已被其他bean注入的那个subBean实例。
- 两个不同的subBean的获取逻辑factoryBean#getObject中的相互循环依赖是无法解决的,因为这种注入对spring来讲有点类似于构造器注入,也就是说这种循环依赖是构造器循环依赖,而且无法使用@Lazy强行切断,所以一定不要写这种代码。