Создание не простое, пожалуйста, указывайте автора в начале перепечатки: 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
Процесс (инициализация одиночного контейнера / несколько экземпляров создаются каждый раз, когда они вызываютсяbean
instance), либо напрямую из кеша первого уровня для получения 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
интерфейс для полученияbeanFactory
instance, а в фабричном методе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 очень хорошо решил эту проблему циклической зависимости.
Давайте просто взглянем на блок-схему:
3.2. Сначала инициализируйтеsubBean
Пример
только сказалsubBean
Сроки инициализации, на самом деле, говорили о нормальномsubBean
Инициализация похожа на ленивую загрузку, то есть она неa
Запускается до инициализации. Но иногда в нашем проекте зависимости экземпляров могут быть не такими четкими.
Предположим, у нас естьc
экземпляр, это зависит отsubBean
экземпляр, в то время какsubBean
пример иa
Экземпляр циркулярных зависимостей. что еслиc
экземпляр предшествуетa
инициализации экземпляра, он появитсяsubBean
экземпляр предшествуетa
Экземпляр инициализирован. благодаря нашему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
проблема~
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
Подставка для фабричного образца.
Читая исходный код, мы знаем:
-
синглтон
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
Дизайн кэша L3 решает большинство круговых зависимостей, в то время какsubBean
с обычнымbean
Циклические зависимости приводят к возможности двухsubBean
предметная проблема,spring
Используйте несколько проверок для отказа от одного из них бесполезныхsubBean
, зарезервировано другимbean
инъекционныйsubBean
пример. -
два разных
subBean
логика приобретенияfactoryBean#getObject
взаимные циклические зависимости вspring
Несколько похожие терминыВнедрение конструктора, то есть эта круговая зависимостьЦиклическая зависимость конструктора, и не может использовать@Lazy
Насильно отрезано, так что не пишите такой код.
Создание не простое, пожалуйста, указывайте автора в начале перепечатки: Nuggets@小西子 + ссылка на источник~
Если вы хотите узнать больше об исходном коде Spring,Нажмите, чтобы перейти к остальной части построчного анализа серии Spring
٩(* ఠO ఠ)=3⁼³₌₃⁼³₌₃⁼³₌₃du ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла ла. . .
Вот блогер-новичок Сяо Сизи, большие парни видели это, пожалуйста, поставьте лайк в левом верхнем углу~~