说完FactoryBean的亿点点细节,Spring对你来说再也牛不起来了

92 篇文章 2 订阅
55 篇文章 0 订阅

一、前言

SpringIOC部分已经差不多讲完了,下一篇会开始讲AOP部分的源码。本篇博文主要是分享一个小甜点给同学们,讲一下FactoryBean这个接口。

这个接口我们日常开发中使用的不多,更多的是第三方框架接入Spring的时候会使用。不过由于这个接口跟我们IOC中承载主要逻辑的BeanFactory长的比较像,所以面试的时候面试官偶尔也会问问这两种有什么区别。

要我说,这两者区别可大了去了,因为他们基本没啥关联,完全是两个东西。

二、FactoryBean的作用

在讲原理之前,我们还是简单的讲一下FactoryBean接口的作用。我们首先看一下FactoryBean的定义:

public interface FactoryBean<T> {
    @Nullable
    T getObject() throws Exception;

    @Nullable
    Class<?> getObjectType();
    // FactoryBean#getObject返回的bean实例是否是单例的
    // 如果是单例,那么FactoryBean#getObject将只会被调用一次
    default boolean isSingleton() {
        return true;
    }
}

然后我们写一个类实现一下这个接口:

public class SubBean {
}
@Service
public class FactoryBeanDemo implements FactoryBean<SubBean> {
    @Override
    public SubBean getObject() throws Exception {
        return new SubBean();
    }
    @Override
    public Class<?> getObjectType() {
        return SubBean.class;
    }
}

我们启动Spring打印一下这个factoryBeanDemo

public void test() {
    applicationContext = new AnnotationConfigApplicationContext("com.xiaoxizi.spring");
    Object subBean = applicationContext.getBean("factoryBeanDemo");
    System.out.println(subBean);
}

输出:

com.xiaoxizi.spring.factoryBean.SubBean@3e0e1046

可以看到,我们通过getBean("factoryBeanDemo")拿到的居然是FactoryB的实例,而不是我们@Service注解标记的FactoryBeanDemo的实例。

这就是FactoryBean接口的用途啦,当我们向spring注册一个FactoryBean时,通过beanName获取到的将是FactoryBean#getObject方法返回的subBean(我们使用subBean来表示factoryBean#getObject的返回对象)实例,而且注意看FactoryBean#isSingleton方法,说明我们也是可以指定getObject方法获取的实例是单例的还是多例的。

那么,在这种情况,我们还能获取到FactoryBeanDemo的实例么?当然也是可以的,只不过我们需要稍微做一点改变:

public void test() {
    applicationContext = new AnnotationConfigApplicationContext("com.xiaoxizi.spring");
    Object subBean = applicationContext.getBean("factoryBeanDemo");
    System.out.println(subBean);

    Object factoryBeanDemo = applicationContext.getBean("&factoryBeanDemo");
    System.out.println(factoryBeanDemo);
}

输出:

com.xiaoxizi.spring.factoryBean.SubBean@3e0e1046
com.xiaoxizi.spring.factoryBean.FactoryBeanDemo@24c1b2d2

也就是说,正常通过beanNameSpring容器中取的话,是只能取到subBean实例的,但是如果在beanName前面加上&符号,使用&beanNameSpring容器中获取,才能获取到FactoryBean实例本身。

三、源码解析

那么Spring是如何支撑FactoryBean的功能的呢?我们还是一起跟源码看一下。我们之前讲bean的生命周期的时候,有讲到单例的bean都是在Spring容器启动的时候就初始化的,那么对于FactoryBean实例,它的FactoryBean#getObject方法也会在Spring容器启动的时候就初始化嘛?subBean实例又储存在哪里呢?带着这些疑问,我们来看一下获取bean的核心逻辑AbstractBeanFactory#doGetBean方法:

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
                          @Nullable final Object[] args, boolean typeCheckOnly) {
    // 转换一下需要获取的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(...);
            }
        }
    }
    }
}

我们可以看到,不管是初始化还是

1. transformedBeanName处理&符号

刚刚在测试的时候,我们有看到,使用getBean("&factoryBeanDemo")是可以获取到factoryBean的实例的,那么对于这个&符号,spring是在transformedBeanName中做初步处理的:

// AbstractBeanFactory#transformedBeanName
protected String transformedBeanName(String name) {
    // canonicalName 主要是通过别名找beanName的逻辑,逻辑也简单,不过我们不关注
    // 就不看了,而且别名其实用的很少
    // 主要看一下 BeanFactoryUtils.transformedBeanName
    return canonicalName(BeanFactoryUtils.transformedBeanName(name));
}
// BeanFactoryUtils#transformedBeanName
public static String transformedBeanName(String name) {
    // 先说一下,这个BeanFactory.FACTORY_BEAN_PREFIX常量就是 & 符号
    if (!name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
        // 如果不是以 & 符号开头,那就直接返回了
        return name;
    }
    return transformedBeanNameCache.computeIfAbsent(name, beanName -> {
        // 把name的所有前置的&符号全部干掉
        // 比如 &&&factoryBean --> factoryBean
        do {
            beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
        }
        while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
        return beanName;
    });
}

也就是说,对于我们getBean("&factoryBeanDemo")的调用,经过transformedBeanName(name)这一步之后,返回的beanName就是"factoryBeanDemo"了。

2. getObjectForBeanInstance获取最终需要返回的bean实例

不管是调用getBean时,是触发创建初始化bean流程(单例容器初始化/多例每次调用都会创建bean实例),还是直接从一级缓存获取到单例实例最终都需要使用获取到的bean实例调用getObjectForBeanInstance获取最终需要返回的bean,而我们的FactoryBean的逻辑就是在这个地方处理的:

// 需要注意的是,这里传入了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("&factoryBeanDemo")获取到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本身。

2.1. getCachedObjectForFactoryBean从缓存获取subBean

可以看到,当调用getObjectForBeanInstance方法的最后一个参数BeanDefinition为空的时候,代表factoryBean实例是已经创建好了,这个时候会通过getCachedObjectForFactoryBean方法尝试直接从缓存中获取subBean对象,这个方法的逻辑很简单:

// 当前类是 FactoryBeanRegistrySupport
private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>(16);
protected Object getCachedObjectForFactoryBean(String beanName) {
    // 直接从缓存中拿了
    return this.factoryBeanObjectCache.get(beanName);
}

如果缓存中有subBean实例,就直接返回这个实例,如果没有,则还会继续走下面的获取subBean的逻辑。

2.2. getObjectFromFactoryBeanfactoryBean获取subBean

假设缓存中还没有subBean实例,那么肯最终都会走到getObjectFromFactoryBean方法,来获取一个subBean对象:

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
    // 注意这个isSingleton是FactoryBean#isSingleton
    // 也就是说factoryBean是单例-containsSingleton(beanName),
    // 且subBean也定义为单例时,才会把subBean缓存起来
    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 {
        // 非单例就直接创建一个了
        Object object = doGetObjectFromFactoryBean(factory, beanName);
        if (shouldPostProcess) {
            try {
                // 调用BeanPostProcessor
                object = postProcessObjectFromFactoryBean(object, beanName);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(...);
            }
        }
        // 返回
        return object;
    }
}

我们简单看一下这个postProcessObjectFromFactoryBean

protected Object postProcessObjectFromFactoryBean(Object object, String beanName) {
    return applyBeanPostProcessorsAfterInitialization(object, beanName);
}

// 其实这个方法就是bean初始化流程中,initializeBean方法里,bean完全初始化完之后调用的埋点方法
// 由于subBean把整个生命周期(初始化、依赖注入)交由factoryBean处理了(即用户自定义)
// 所以它只需要再调用这个埋点就行
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
    throws BeansException {

    Object result = existingBean;
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        Object current = processor.postProcessAfterInitialization(result, beanName);
        if (current == null) {
            return result;
        }
        result = current;
    }
    return result;
}

可以看到,其实最终调用到的applyBeanPostProcessorsAfterInitialization方法就是bean初始化流程中,initializeBean方法里,bean完全初始化完之后调用的埋点方法。aop也是在这个埋点做操作的,所以我们的subBean也是能使用aop的功能的。

3. subBean的初始化时机

我们已经了解了getBean中对factoryBean的处理逻辑,简单的来讲,其实就是针对传入的name是否有&前缀,来走不同的分支逻辑。

那么现在又有一个问题了,单例的subBean对象,到底是在什么时候创建并且被spring管理起来的呢?

我们知道,如果subBean缓存factoryBeanObjectCache中没有对于的subBean,那么直接调用getBean("factoryBeanDemo")肯定是会创建一个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;
    }
}

4. subBean的循环依赖问题

我们之前讲循环依赖的时候,都是基于两个普通的bean来讲解的,而循环依赖现象是指spring在进行单例bean的依赖注入时,出现A->BB->A的问题。

同学们可能会说,subBean的依赖注入都不归spring管理了,怎么还能出现循环依赖问题的?

首先需要明确一点的是,循环依赖其实跟spring没有关系的,只要出现了A->BB->A的情况,我们就认为AB实例出现了循环依赖。而spring只是在它的管理的范围内,巧妙的使用了三级缓存/@Lazy解决了循环依赖而已。

而由于factoryBean实例本身就是由spring容器管理的,那么我们做以下操作,也是合理的:

@Getter
@ToString
@AllArgsConstructor
public class SubBean {
    private A a;
}

@Component
public class A {
    @Autowired
    private SubBean subBean;
}

@Service
public class FactoryBeanDemo implements FactoryBean<SubBean>, BeanFactoryAware {
    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(final BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public SubBean getObject() throws Exception {
        final A bean = this.beanFactory.getBean(A.class);
        return new SubBean(bean);
    }
}

我们factoryBean通过BeanFactoryAware接口拿到beanFactory实例,并且在工厂方法getObject获取subBean的流程中使用beanFactory.getBean(A.class)spring容器中获取a实例,而a实例又是依赖subBean实例的...

有同学可能会觉得我在难为spring,为什么要强行用这么复杂的结构,来构建一个循环依赖呢?

可是a实例和subBean最终不都是由spring管理么?它不应该解决这个问题么?

当然是可以解决的,不过这个地方要分两种情况讨论。

接下来的讨论会涉及到 spring三级循环依赖的原理

3.1. 先初始化a实例

对于先初始化a实例的场景,其实spring原有的三级缓存设计就可以很好的解决这个问题。同学们可以回想一下,我们在创建a实例之后,尚未进行依赖注入subBean之前,就把a实例暴露到缓存了。而注入subBean的时候,会触发FactoryBean#getObject方法,最终会调用到我们自己写的beanFactory.getBean(A.class)的逻辑,从缓存中获取到暴露到缓存的a实例。

那么按这个流程下来,其实整体是没问题的,spring的三级缓存的设计已经很好的解决了这种循环依赖的问题。

我们还是简单的看一下流程图:


factoryBean循环依赖问题-先初始化A

3.2. 先初始化subBean实例

刚刚讲subBean的初始化时机时,其实有讲过,正常的subBean的初始化是一种类似于懒加载的方式,也就是说它是不会在a初始化化之前触发初始化的。但是有时候我们的项目中,实例的依赖关系可能不是这么清晰的。

假设我们有一个c实例,它依赖subBean实例,而subBean实例又和a实例循环依赖。那如果c实例先于a实例初始化,就会出现subBean实例先于a实例初始化的情况了。由于我们的subBean是没有多级缓存的机制来解决循环依赖问题,那么这个时候,整个初始化流程就变成了:


factoryBean循环依赖问题-先初始化subBean

可以看到,如果没有特殊处理的话,尽管由于我们的普通bean有三级缓存的设计,不会出现完全无法解决的级联创建实例问题。但是,也会导致我们的factoryBean#getObject被调用两次,生成两个subBean对象,且最终factoryBeanObjectCache缓存中的subBean1对象与a实例中注入的subBean2对象不是同一个。

那么这个情况应该如何解决呢?有同学可能会说,使用多级缓存呀,和普通的bean一个思路就可以了。

但是,多级缓存的思路,其实主要就是在bean实例创建之后,依赖注入之前,将bean实例暴露到缓存中,进而解决循环依赖的问题。然而,我们刚刚举例中,实际是在factoryBean#getObject获取subBean实例的过程中进行了依赖注入(虽然是我们手动的调用beanFactory.getBean获取的依赖),这个情况其实有点类似于构造器注入依赖了,构造器循环依赖用多级缓存的思想也解决不了哇。那么对于两个subBean实例的问题,spring是怎么解决的呢?spring通过短短几行代码,就解决了这个问题:

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
    // 注意这个isSingleton是FactoryBean#isSingleton
    // 也就是说factoryBean是单例-containsSingleton(beanName),
    // 且subBean也定义为单例时,才会把subBean缓存起来
    if (factory.isSingleton() && containsSingleton(beanName)) {
        synchronized (getSingletonMutex()) {
            // 加锁
            // 先从缓存拿一次
            Object object = this.factoryBeanObjectCache.get(beanName);
            if (object == null) {
                // 确保缓存没有,才创建一个
                // 这里就是简单的调用FactoryBean#getObject了,就不往下跟了
                object = doGetObjectFromFactoryBean(factory, beanName);
                Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                if (alreadyThere != null) {
                    // 再从缓存中获取一遍,如果缓存中存在对象了,则把当前对象覆盖
                    // 并且会跳过subBean的beanPostProcessor调用流程
                    // 这里其实是用来解决循环依赖问题的
                    object = alreadyThere;
                }
                else {
                    // 跳过调用beanPostProcessor的逻辑
                    this.factoryBeanObjectCache.put(beanName, object);
                }
            }
            return object;
        }
    }
    // 跳过
}

这里再factoryBean#getObject方法获取到subBean1之后,再次从factoryBeanObjectCache获取了一遍subBean实例,如果获取到了subBean2,其实就代表出现我们举例的那种循环依赖了,导致缓存中已经有subBean实例了。此时会把subBean2赋值给object并且返回出去,subBean1就直接丢弃掉了,也不会放入缓存。这样就巧妙的解决了两个subBean的问题啦~


factoryBean循环依赖问题-解决方案

3.3. 无法解决的循环依赖问题

刚刚我们有聊到,factoryBean#getObject中使用beanFactory#getBean进行依赖注入,本质上相当于是构造器注入。

而上一篇讲循环缓存的时候,我们也有讲过,正常情况下来讲,构造器循环依赖是无法解决的:

@Getter
@ToString
@AllArgsConstructor
public class SubBean {
    private A a;
}

@Component
public class A {
    public A(final SubBean subBean) {
        this.subBean = subBean;
    }
    private SubBean subBean;
}

@Service
public class FactoryBeanDemo implements FactoryBean<SubBean>, BeanFactoryAware {
    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(final BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public SubBean getObject() throws Exception {
        final A bean = this.beanFactory.getBean(A.class);
        return new SubBean(bean);
    }
}

我们启动一下:

public void test() {
    applicationContext = new AnnotationConfigApplicationContext("com.xiaoxizi.spring");
    Object subBean = applicationContext.getBean("factoryBeanDemo");
    System.out.println(subBean);
}

肯定是会直接报错的:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': Requested bean is currently in creation: Is there an unresolvable circular reference?

当然我们还是可以使用@Lazy解决这个问题:

@Component
public class A {
    public A(@Lazy final SubBean subBean) {
        this.subBean = subBean;
    }
    private SubBean subBean;
}

这种情况下,spring是能正常运行的,因为我们使用@Lazy切断了循环依赖链。

那么接下来我要说的是,正真完全无法解决的循环依赖问题:

@AllArgsConstructor
public class SubBeanA {
    private SubBeanB b;
}

@AllArgsConstructor
public class SubBeanB {
    private SubBeanA a;
}

@Service
public class FactoryBeanA implements FactoryBean<SubBeanA>, BeanFactoryAware {
    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(final BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public SubBeanA getObject() throws Exception {
        final SubBeanB bean = (SubBeanB)this.beanFactory.getBean("factoryBeanB");
        return new SubBeanA(bean);
    }
}

@Service
public class FactoryBeanB implements FactoryBean<SubBeanB>, BeanFactoryAware {
    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(final BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public SubBeanB getObject() throws Exception {
        final SubBeanA bean = (SubBeanA)this.beanFactory.getBean("factoryBeanA");
        return new SubBeanB(bean);
    }
}

这种情况下启动会直接栈溢出的,连BeanCurrentlyInCreationException异常都不会有。主要原因是spring是在调用完factoryBean#getObject之后再使用singletonsCurrentlyInCreation容器进行循环依赖检测的,而这种循环依赖,其实是在疯狂的调用factoryBeanA#getObject -> factoryBeanB#getObject -> factoryBeanA#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 alreadyThere = this.factoryBeanObjectCache.get(beanName);
                if (alreadyThere != null) {
                    object = alreadyThere;
                }
                else {
                    // 正常要走这里
                    if (shouldPostProcess) {
                        // 在这里才做循环依赖检测
                        if (isSingletonCurrentlyInCreation(beanName)) {
                            return object;
                        }
                        // 在这里才做循环依赖检测
                        beforeSingletonCreation(beanName);
                        try {
                            object = postProcessObjectFromFactoryBean(object, beanName);
                        }
                        catch (Throwable ex) {
                            throw new BeanCreationException(...);
                        }
                        finally {
                            afterSingletonCreation(beanName);
                        }
                    }
                    if (containsSingleton(beanName)) {
                        this.factoryBeanObjectCache.put(beanName, object);
                    }
                }
            }
            return object;
        }
    }
    // 跳过
}

所以,同学们千万不要写刚刚示例中这种代码呀,一定一定会被开除哒~

四、小结

本篇博文主要讲了一下spring中的FactoryBean接口。这个接口其实是spring对工厂模式的一种支持。

通过阅读源码,我们知道了:

  1. 单例的factoryBean对象本身会在spring容器启动时主动初始化。而subBean的初始化则是在第一次需要获取时才会触发。
  2. 如果factoryBean对象实现的接口是SmartFactoryBeanisEagerInit方法返回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强行切断,所以一定不要写这种代码。

最后

大家看完有什么不懂的可以在下方留言讨论.
谢谢你的观看。
觉得文章对你有帮助的话记得关注我点个赞支持一下!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值