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、总结:

  1.  单例的factoryBean对象本身会在spring容器启动时主动初始化。而subBean的初始化则是在第一次需要获取时才会触发。
  2.  如果factoryBean对象实现的接口是SmartFactoryBean且isEagerInit方法返回true,那么subBean对象也会在spring容器启动的时候主动初始化。 
  3.  如果bean注册的时候,beanName对应的bean实例是一个factoryBean,那么我们通过getBean(beanName)获取到的对象将会是subBean对象;如果要获取工厂对象factoryBean,需要使用getBean("&" + beanName)。
  4. 单例的subBean也会缓存在spring容器中,具体的容器是FactoryBeanRegistrySupport#factoryBeanObjectCache,一个Map<beanName, subBean实例>。 
  5. Spring的三级缓存设计解决了大部分循环依赖问题,而对与subBean与普通bean的循环依赖导致可能出现两个subBean对象的问题,spring采用多重检查的方式,丢弃掉其中一个无用的subBean,保留已被其他bean注入的那个subBean实例。 
  6. 两个不同的subBean的获取逻辑factoryBean#getObject中的相互循环依赖是无法解决的,因为这种注入对spring来讲有点类似于构造器注入,也就是说这种循环依赖是构造器循环依赖,而且无法使用@Lazy强行切断,所以一定不要写这种代码。






posted @ 2023-11-27 22:19  邓维-java  阅读(786)  评论(0)    收藏  举报