Интерпретация Spring (6) построчно — 100 миллионов деталей FactoryBean

Java

Создание не простое, пожалуйста, указывайте автора в начале перепечатки: Nuggets@小西子 + ссылка на источник~

Если вы хотите узнать больше об исходном коде Spring,Нажмите, чтобы перейти к остальной части построчного анализа серии Spring

Введение

SpringизIOCЧасть почти закончена, начинается следующая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;
    }
}

Давайте начнем весну и распечатайте это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

То есть через обычныйbeanNameотSpringЕсли вы берете его из контейнера, вы можете взять его толькоsubBeanэкземпляр, но если вbeanNameдобавленный&символ, использование&beanNameотSpringможно получить в контейнереFactoryBeanсам экземпляр.

3. Анализ исходного кода

Так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Процесс (инициализация одиночного контейнера / несколько экземпляров создаются каждый раз, когда они вызываютсяbeaninstance), либо напрямую из кеша первого уровня для получения singleton instance нужно использовать полученный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. getObjectFromFactoryBeanотfactoryBeanПолучать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Логика такова:

если правильноbeanуровень жизненного циклаspringНе знаком с процессом запуска контейнера(начальство)(Вниз)

// 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->B,B->AПроблема.

Студенты могут сказать,subBeanВнедрение зависимостей не возвращаетсяspringМенеджмент, как еще может быть проблема круговой зависимости?

Прежде всего, должно быть ясно, что циклические зависимости на самом деле связаны сspringНеважно, лишь бы появилосьA->B,B->Aслучае мы предполагаем, чтоA,BЭкземпляр имеет циклическую зависимость. и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интерфейс для полученияbeanFactoryinstance, а в фабричном методе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Дизайн кэша L3 очень хорошо решил эту проблему циклической зависимости.

Давайте просто взглянем на блок-схему: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;
        }
    }
    // 跳过
}

Так что, студенты, не пишите сейчас код в примере, вас точно уволят~

4. Резюме

Этот пост в блоге в основном говорит оspringсерединаFactoryBeanинтерфейс. Этот интерфейс на самом делеspringПодставка для фабричного образца.

Читая исходный код, мы знаем:

  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Дизайн кэша L3 решает большинство круговых зависимостей, в то время какsubBeanс обычнымbeanЦиклические зависимости приводят к возможности двухsubBeanпредметная проблема,springИспользуйте несколько проверок для отказа от одного из них бесполезныхsubBean, зарезервировано другимbeanинъекционныйsubBeanпример.

  6. два разныхsubBeanлогика приобретенияfactoryBean#getObjectвзаимные циклические зависимости вspringНесколько похожие терминыВнедрение конструктора, то есть эта круговая зависимостьЦиклическая зависимость конструктора, и не может использовать@LazyНасильно отрезано, так что не пишите такой код.

Создание не простое, пожалуйста, указывайте автора в начале перепечатки: Nuggets@小西子 + ссылка на источник~

Если вы хотите узнать больше об исходном коде Spring,Нажмите, чтобы перейти к остальной части построчного анализа серии Spring

٩(* ఠO ఠ)=3⁼³₌₃⁼³₌₃⁼³₌₃du ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла. . .

Вот блогер-новичок Сяо Сизи, большие парни видели это, пожалуйста, поставьте лайк в левом верхнем углу~~