一文搞懂Spring是如何解决Bean循环依赖的?

news2024/11/19 19:19:35

一.什么是Bean循环依赖

循环依赖是指Bean对象循环引用,是两个或多个Bean之间相互持有对方的引用,循环依赖有2中表现形式

第一种相互依赖,就是A依赖B,B又依赖A

第二种是自我依赖,就是A依赖自己形成自我依赖

对象引用循环依赖再某些业务场景上可能是合理存在的,但是由于Spring容器涉及了依赖注入机制,即Spring容器在创建bean实例化以后就要给bean中的属性自动复制,要全部自动赋值之后,才能交给用户使用,如果出现循环依赖的情况,以两个Bean互相依赖的情况作为举例,假设又Aservice 已经实例化,但是还没有完程初始化,但是AService需要自动赋值的Bservice并没有初始化,如果Spring立刻初始化Bservice,发现Bservice中需要自动赋值Aservice也没有初始化完成,这样就会出现相互等待,形成自循环,可能导致Spring容器都无法启动了。

二. Spring创建Bean的主要流程

Spring创建Bean的地方是在AbstractAutowireCapableBeanFactory的doCreateBean方法

	protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
			throws BeanCreationException {

		// Instantiate the bean.
        //这个beanWrapper是用来持有创建出来的bean对象的
		BeanWrapper instanceWrapper = null;
        //获取factoryBean实例缓存
		if (mbd.isSingleton()) {
         //如果是单例对象,从factorybean实例缓存中移除当前bean定义信息
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
		}
        //没有就创建实例
		if (instanceWrapper == null) {
        // 根据执行bean使用对应的策略创建新的实例 如 工厂发发,构造函数主动注入,简单实例化
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}
        //从包装类中获取原始bean
		final Object bean = instanceWrapper.getWrappedInstance();
        //获取具体的bean对应的Class属性
		Class<?> beanType = instanceWrapper.getWrappedClass();
        //如果不等于NullBean类型,那么修改目标类型
		if (beanType != NullBean.class) {
			mbd.resolvedTargetType = beanType;
		}

		// Allow post-processors to modify the merged bean definition.
		synchronized (mbd.postProcessingLock) {
			if (!mbd.postProcessed) {
				try {
                    //MergedBeanDefinitionPostPorcessor后置处理器修改合并bean的定义
					applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
				}
				catch (Throwable ex) {
					throw new BeanCreationException(mbd.getResourceDescription(), beanName,
							"Post-processing of merged bean definition failed", ex);
				}
				mbd.postProcessed = true;
			}
		}

		// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.
    //判断当前bean是否需要提前曝光:单一&允许循环依赖&当前bean正在创建中,检测循环依赖
		boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
				isSingletonCurrentlyInCreation(beanName));
		if (earlySingletonExposure) {
			if (logger.isTraceEnabled()) {
				logger.trace("Eagerly caching bean '" + beanName +
						"' to allow for resolving potential circular references");
			}
//为避免后期循环依赖,可以在bean初始化完成前将创建实例的ObjectFactory加入工厂
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

		// Initialize the bean instance.
//初始化bean实例
		Object exposedObject = bean;
		try {
            //对bean的属性进行填充,将各个属性注入,其中,可能存在依赖于其他bean的属性,则会递归初始化依赖的bean
			populateBean(beanName, mbd, instanceWrapper);
//执行初始化逻辑
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}
		catch (Throwable ex) {
			if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
				throw (BeanCreationException) ex;
			}
			else {
				throw new BeanCreationException(
						mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
			}
		}

		if (earlySingletonExposure) {
//从缓存中获取具体的对象
			Object earlySingletonReference = getSingleton(beanName, false);
//earlySingletoneference只有在检测到有循环依赖的情况才会不为空
			if (earlySingletonReference != null) {
// 如果exposedObject没有在初始化方法中被改变,也就是没有被增强
				if (exposedObject == bean) {
					exposedObject = earlySingletonReference;
				}
				else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
					String[] dependentBeans = getDependentBeans(beanName);
					Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
					for (String dependentBean : dependentBeans) {
 返回false说明依赖还没实例化好
						if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
							actualDependentBeans.add(dependentBean);
						}
					}
//因为bean创建后所依赖的bean一定是已经创建的
//actualDependentBeans不会空则标识当前bean创建后其依赖的bean却没有全部创建完,也就是说存在循环依赖
					if (!actualDependentBeans.isEmpty()) {
						throw new BeanCurrentlyInCreationException(beanName,
								"Bean with name '" + beanName + "' has been injected into other beans [" +
								StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
								"] in its raw version as part of a circular reference, but has eventually been " +
								"wrapped. This means that said other beans do not use the final version of the " +
								"bean. This is often the result of over-eager type matching - consider using " +
								"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
					}
				}
			}
		}

		// Register bean as disposable.
		try {
// 注册bean对象,方便后续在容器销毁的时候销毁对象
			registerDisposableBeanIfNecessary(beanName, bean, mbd);
		}
		catch (BeanDefinitionValidationException ex) {
			throw new BeanCreationException(
					mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
		}

		return exposedObject;
	}

粗分以下,首先调用createBeanInstance实例化,然后applyMergedBeanDefinitionPostProcessors 这里是AOP的核心处理方法,继续会将当前对象加入到三级缓存中,连同lambda表达式,为了解决循环依赖问题,继续到了populateBean方法,对bean的属性填充,继续到了initializeBean方法,初始化Bean对象,然后调用registerDisposableBeanIfNecessary销毁对象

这里面我们需要关注三点


1. 创建实例化Bean

instanceWrapper = createBeanInstance(beanName, mbd, args);

   主要就是通过反射调用默认构造函数创建Bean的实例,此时bean的属性都还是默认值null,被逐节@Bean标注的方法就是此阶段被调用的

2.填充属性

populateBean(beanName, mbd, instanceWrapper);

       这一步主要是对bean的依赖属性进行填充,对@Value @Autowired @Resource注解标注的属性注入对象引用

3.完成bean的初始化

exposedObject = initializeBean(beanName, exposedObject, mbd);

调用皮质指定中的init方法,如xml文件指定bean的init-method方法或注解@Bean(initMethod = 'initMethod')指定的方法

三.Bean创建过程BeanPostProcessor接口扩展点

在Bean创建的流程中Spring提供了多个BeanPostProcessor接口(BPP)方便开发者对Bean进行自定义调整和加工。有以下几种BPP接口比较常用

  • postProcessMergedBeanDefinition:可对BeanDefinition添加额外的自定义配置
  • getEarlyBeanReference:返回早期暴露的bean引用,一个典型的例子是循环依赖时如果有动态代理,需要再次先返回代理实例
  • postProcessAfterInstantiation:在populateBean前用户可以手动注入一些属性
  • postProcessProperties: 对属性进行注入,例如配置文件加密信息再次解密后注入
  • postProcessBeforeInitialization:属性注入后的一些额外操作
  • postProcessAfterInitialization :实力完成创建的最后一部,也是一些BPP进行AOP代理的时机。

流程图总结

图片

 1.实例化Bean细节

调用的是AbstractAutowireCapableBeanFactory的createBeanInstance

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
		// Make sure bean class is actually resolved at this point.
        //确认需要创建bean实例的类可以实例化
		Class<?> beanClass = resolveBeanClass(mbd, beanName);
// 确保class不为空,并且访问权限是public
		if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
			throw new BeanCreationException(mbd.getResourceDescription(), beanName,
					"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
		}
// 判断当前beanDefinition中是否包含实例供应器,此处相当于一个回调方法,利用回调方法来创建bean
		Supplier<?> instanceSupplier = mbd.getInstanceSupplier();
		if (instanceSupplier != null) {
			return obtainFromSupplier(instanceSupplier, beanName);
		}
// 如果工厂方法不为空则使用工厂方法初始化策略
		if (mbd.getFactoryMethodName() != null) {
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}
// 一个类可能有多个构造器,所以Spring得根据参数个数、类型确定需要调用的构造器
		// 在使用构造器创建实例后,Spring会将解析过后确定下来的构造器或工厂方法保存在缓存中,避免再次创建相同bean时再次解析

		// Shortcut when re-creating the same bean...
// 标记下,防止重复创建同一个bean
		boolean resolved = false;
// 是否需要自动装配
		boolean autowireNecessary = false;
// 如果没有参数
		if (args == null) {
			synchronized (mbd.constructorArgumentLock) {
// 因为一个类可能由多个构造函数,所以需要根据配置文件中配置的参数或传入的参数来确定最终调用的构造函数。
				// 因为判断过程会比较,所以spring会将解析、确定好的构造函数缓存到BeanDefinition中的resolvedConstructorOrFactoryMethod字段中。
				// 在下次创建相同时直接从RootBeanDefinition中的属性resolvedConstructorOrFactoryMethod缓存的值获取,避免再次解析
				if (mbd.resolvedConstructorOrFactoryMethod != null) {
					resolved = true;
					autowireNecessary = mbd.constructorArgumentsResolved;
				}
			}
		}
// 有构造参数的或者工厂方法
		if (resolved) {
// 构造器有参数
			if (autowireNecessary) {
// 构造函数自动注入
				return autowireConstructor(beanName, mbd, null, null);
			}
			else {
// 使用默认构造函数构造
				return instantiateBean(beanName, mbd);
			}
		}

		// Candidate constructors for autowiring?
// 从bean后置处理器中为自动装配寻找构造方法, 有且仅有一个有参构造或者有且仅有@Autowired注解构造
		Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
// 以下情况符合其一即可进入
		// 1、存在可选构造方法
		// 2、自动装配模型为构造函数自动装配
		// 3、给BeanDefinition中设置了构造参数值
		// 4、有参与构造函数参数列表的参数
		if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
				mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
			return autowireConstructor(beanName, mbd, ctors, args);
		}

		// Preferred constructors for default construction?
// 找出最合适的默认构造方法
		ctors = mbd.getPreferredConstructors();
		if (ctors != null) {
// 构造函数自动注入
			return autowireConstructor(beanName, mbd, ctors, null);
		}

		// No special handling: simply use no-arg constructor.
// 使用默认无参构造函数创建对象,如果没有无参构造且存在多个有参构造且没有@AutoWired注解构造,会报错
		return instantiateBean(beanName, mbd);
	}

首先获取Class对象,继续往下,if用来判断访问权限是否是public,否则报错,getInstanceSupplier方法,实际上supplier是一个函数式接口,所以会调用get方法,就是到实际上传进来的方法,这个用getInstanceSupplier时,发现instanceSupplier为空,那么如何让其有值呢?

因为这个方法时在db中的有getInstanceSupplier就会有SetInstanceSupplier,从这点入手找到该类为abstractBeanDefinition),如果想修改bd,只能在BFPP里面做相关实现了。(supplier,用的genicBeanDefinition里的setInstanceSupplier)
继续往下,有个factoryMethod创建对象的方式。instantiateUsingFactoryMethod。这种也可以提前实力化对象。

再往下就是构造器的选用以及实例化的过程了

resolvedConstructorOrFactoryMethod这个可以认为缓存,如果需要多次取构造器,那么可以不用再次通过选择构造器操作,在这里可以直接拿到构造器。

然后看到determineConstructorsFromBeanPostProcessors方法。这个方法从bean后置处理器中为自动装配寻找构造方法, 有且仅有一个有参构造或者有且仅有@Autowired注解构造才能有效果。为什么我们可以从BeanPostProcessor中可以获得构造器?因为有个smartInstationAwareBeanPostProcessor接口,这个接口有个方法determineCandidateConstructors,有个方法实现AutowiredAnnotationBeanPostProcessor,这个方法主要筛选了一些构造方法。排除了一些错误使用@autowired方法的情况。

接下来到instantiateBean,实例化的过程。

获取实例化策略,再instantiale,实例化策略默认用的cglib这个类,这里有一个接口,instantiationStrategy,默认调用的时CglibSubclassingInstantiationStrategy,并且集成了SimpleInstantiationStrategy

 

 这就是实例化

2.填充bean属性

如果bean的属性有@Autowired要注入的属性,则会进行属性填充

进行属性填充的前提是要保证属性实力已经存在spring容器中,如果不存在则会先去加载属性

3.初始化bean

 1.调用invokeAwareMethods

如果bean 实现BeanNameAware接口,调用setBeanName

实现BeanClassLoaderAware接口,调用setBeanClassLoader方法

实现BeanFactoryAware接口,调用setBeanFactory方法

2.调用applyBeanPostProcessorsBeforeInitialization

 循环调用实现了BeanPostProcessors接口的postProcessBeforeIntialization方法,由于Spring自带了ApplicationContestAqareProcessor类重写了postProcessBeforeInitialization方法,则会优先循环到ApplicationContextAwareProcessor的postProcessBeforeInitialization方法

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        AccessControlContext acc = null;
        if (System.getSecurityManager() != null && (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware || bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware || bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
            acc = this.applicationContext.getBeanFactory().getAccessControlContext();
        }

        if (acc != null) {
            AccessController.doPrivileged(() -> {
                this.invokeAwareInterfaces(bean);
                return null;
            }, acc);
        } else {
            this.invokeAwareInterfaces(bean);
        }

        return bean;
    }

执行到了ApplicationContextAwareProcessor的postProcessBeforeInitialization方法,会检查是否实现Aware接口,这里重点关注Aware接口的ApplicationContextAware,如果实现了ApplicationContextAware接口,就会调用setApplicationContext方法

    private void invokeAwareInterfaces(Object bean) {
        if (bean instanceof Aware) {
            if (bean instanceof EnvironmentAware) {
                ((EnvironmentAware)bean).setEnvironment(this.applicationContext.getEnvironment());
            }

            if (bean instanceof EmbeddedValueResolverAware) {
                ((EmbeddedValueResolverAware)bean).setEmbeddedValueResolver(this.embeddedValueResolver);
            }

            if (bean instanceof ResourceLoaderAware) {
                ((ResourceLoaderAware)bean).setResourceLoader(this.applicationContext);
            }

            if (bean instanceof ApplicationEventPublisherAware) {
                ((ApplicationEventPublisherAware)bean).setApplicationEventPublisher(this.applicationContext);
            }

            if (bean instanceof MessageSourceAware) {
                ((MessageSourceAware)bean).setMessageSource(this.applicationContext);
            }

            if (bean instanceof ApplicationContextAware) {
                ((ApplicationContextAware)bean).setApplicationContext(this.applicationContext);
            }
        }

    }

 最后调用我们自定义实现的BeanPostProcessor接口,调用postProcessBeforeInitialization方法。

3.调用invokeInitMethods方法

	protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
			throws Throwable {

		boolean isInitializingBean = (bean instanceof InitializingBean);
		if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
			if (logger.isTraceEnabled()) {
				logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
			}
			if (System.getSecurityManager() != null) {
				try {
					AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
						((InitializingBean) bean).afterPropertiesSet();
						return null;
					}, getAccessControlContext());
				}
				catch (PrivilegedActionException pae) {
					throw pae.getException();
				}
			}
			else {
				((InitializingBean) bean).afterPropertiesSet();
			}
		}

		if (mbd != null && bean.getClass() != NullBean.class) {
			String initMethodName = mbd.getInitMethodName();
			if (StringUtils.hasLength(initMethodName) &&
					!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
					!mbd.isExternallyManagedInitMethod(initMethodName)) {
				invokeCustomInitMethod(beanName, bean, mbd);
			}
		}
	}

判断你是否实现了InitializingBean接口,如果实现了InitializingBean接口 就调用afterPropertiesSet()方法

之后指定init-method方法,调用init-method方法

4.调用applyBeanPostProcessorsAfterInitialization()

循环调用实现了BeanPostProcessor接口的postProProcessAfterinitialization方法

动态代理埋点 

 Spring的动态代理(AOP)是通过BPP中调用applyBeanPostProcessorsAfterinitialization(),其中AbstractAutoProxyCreator是十分典型的自动代理类,它实现了SmartInstantiationAwareBeanPostProcessor接口,并且重写了getEarlyBeanReference和postProcessAfterInitialization两个方法实现代理的逻辑,这样完成对原始Bean进行增强,生成新Bean对象,将增强后的新Bean对象注入到属性依赖中。

四.Spring如何解决循环依赖的?

先说以下结论吧,是通过三级缓存和提前暴露的机制来解决循环依赖的。

1.三级缓存的作用

三级缓存就是三个map来存储不同阶段Bean对象

1. 一级缓存 singletonObjects

主要存放的是已经完成实例化,属性填充和初始化所有步骤的单例Bean实例,这样的Bean能够直接给用户使用,称为成熟bean

2.二级缓存 earlySingletonObjects

主要存放的是已经完成初始化但属性还没有自动赋值的bean,这些bean还不能给用户使用,只是用于提前暴露的Bean实例,我们把这样的bean称之为临时Bean或者早期bean

3.三级缓存 singletonFactories

存放的是ObjectFactory的匿名内部类实例,调用ObjectFactory.getObject()最终hi调用getEarlyBeanReference方法,该方法可以获取提前暴露的单例bean引用

4.三级缓存解决循环依赖的过程

通过源码分析一下Spring是如何运用三机缓存来解决循环依赖的,Spring创建Bean的核心代码是AbstractBeanFactory类中的doGetBean方法,在实例化bean之前,会首先尝试从三级缓存中获取bean,这也就是解决循环依赖的开始

假设我们现在是AService依赖BService ,BService依赖AService

开始加载Aservice Bean首先一次从一二三级缓存中查找是否存在beanName=AService的对象


// AbstractBeanFactory.java
    protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
                              @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
        final String beanName = transformedBeanName(name);
        // 1.尝试从缓存中获取bean,AService还没创建三级缓存都没命中
        Object sharedInstance = getSingleton(beanName);
        if (mbd.isSingleton()) {
              
            sharedInstance = getSingleton(beanName,    () -> {  //注意此处参数是一个lambda表达式即参数传入的是ObjectFactory类型一个匿名内部类对象
                                                        try {
                                                            return createBean(beanName, mbd, args);  // 
                                                        }
                                                        catch (BeansException ex) {}
                                                    });
            beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
        }
    }
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		//从一级缓存中获取Bean
		Object singletonObject = this.singletonObjects.get(beanName);
		如果没有 并且判断这个bean是否正在被创建
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
			synchronized (this.singletonObjects) {
				//从二级缓存才那种获取bean
				singletonObject = this.earlySingletonObjects.get(beanName);
				if (singletonObject == null && allowEarlyReference) {
					//从三级缓存中获取bean
					ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
					if (singletonFactory != null) {
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

因为Aservice还没创建三级缓存,都没有命中,所以走到创建Bean的代码逻辑,调用方法getSingleton(String beanName,ObjectFactory objectFactory)方法,第二个参数传入一个ObjectFactory接口的匿名内部类实例。

if (mbd.isSingleton()) {
					sharedInstance = getSingleton(beanName, () -> {
						try {
							return createBean(beanName, mbd, args);
						}
						catch (BeansException ex) {
							// Explicitly remove instance from singleton cache: It might have been put there
							// eagerly by the creation process, to allow for circular reference resolution.
							// Also remove any beans that received a temporary reference to the bean.
							destroySingleton(beanName);
							throw ex;
						}
					});
					bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
				}

public Object getSingleton(String beanName, ObjectFactory singletonFactory) {
//将当前beanName放到singletonsCurrentlyInCreation 集合中,标识该bean正在创建
    beforeSingletonCreation(beanName);
    //通过回调getObject()方法触发AbstractAutowireCapableBeanFactory#createBean(String beanName, RootBeanDefinition mbd, Object[] args)的执行
    singletonObject = singletonFactory.getObject();
    afterSingletonCreation(beanName);
    addSingleton(beanName, singletonObject);
}

该方法主要做四件事情

将当前beanName放到singletonsCurrentlyInCreation集合中标识该bean正在被创建

调用匿名内部类实例对象的getObject()方法触发AbstractAutowireCapableBeanFactory#createBean方法的执行;

将当前beanName从singletonsCurrentlyInCreation移除

singletonFactory.getObject() 方法触发回调AbstractAutowireCapableBeanFactory#createBean(String beanName, RootBeanDefinition mbd, Object[] args)的执行,走真正创建AService Bean流程。

//真正创建Bean的地方 AbstractAutowireCapableBeanFactory#doCreateBean
    protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)  throws BeanCreationException {

        // Instantiate the bean.
        BeanWrapper instanceWrapper = null;
        if (mbd.isSingleton()) {
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }
        // bean初始化第一步:默认调用无参构造实例化Bean
        // 构造参数依赖注入,就是发生在这一步
        if (instanceWrapper == null) {
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        // 实例化后的Bean对象
        final Object bean = instanceWrapper.getWrappedInstance();
        // 将刚创建的bean放入三级缓存中singleFactories(key是beanName,value是ObjectFactory)
        //注意此处参数又是一个lambda表达式即参数传入的是ObjectFactory类型一个匿名内部类对象,在后续再缓存中查找Bean时会触发匿名内部类getEarlyBeanReference()方法回调
        addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        // Initialize the bean instance.
        Object exposedObject = bean;
        try {
            // bean创建第二步:填充属性(DI依赖注入发生在此步骤)
            populateBean(beanName, mbd, instanceWrapper);
            // bean创建第三步:调用初始化方法,完成bean的初始化操作(AOP的第三个入口)
            // AOP是通过自动代理创建器AbstractAutoProxyCreator的postProcessAfterInitialization()
//方法的执行进行代理对象的创建的,AbstractAutoProxyCreator是BeanPostProcessor接口的实现
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        catch (Throwable ex) {
            // ...
        }

    }

在上面创建AService Bean代码流程中可以看出,AService实例化后,polulateBean注入属性之前调用addSingletonFactory(Strng beanName,ObjectFactory singletonFactory)方法将以key为AService,value是ObjectFactory类型的一个匿名内部类对象放到三级缓存中,在后续使用AService时会依次从一二三级缓存中查找, 最终三级缓存中查找到这个匿名内部类对象,从而触发匿名内部类中的getEarlyBeanReference()回调。

这块为什么不是AService直接放入三级缓存呢?因为AOP增强逻辑是在初始化Bean的第三步,也就是在initializeBean方法里面最后一步的applyBeanPostProcessorsAfterInitialization方法中,调用初始化方法之后进行的,AOP增强后生成的新代理类AServiceProxy实例对象,假如此时直接把AService实例直接放入三级缓存中,那么在对BService Bean依赖的AService属性赋值的就是Aservice实例,而不是增强后的AServiceProxy实例对象

在以Key为AService,value为ObjectFactory类型一个匿名内部类对象放入三级缓存后,继续对AService进行属性填充(依赖注入),这时发现AService依赖BService。

于是又依次从一二三级缓存中查询BService Bean,没找到,于是又按照上述的流程实例化BService,将以Key为BService,value是ObjectFactory类型一个匿名内部类对象放入三级缓存中,继续对BService进行属性填充(依赖注入),这时发现BService又依赖AService。于是依次在一二三级缓存中查找AService。

//DefaultSingletonBeanRegistry.java
public Object getSingleton(String beanName) {
    return getSingleton(beanName, true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // 从一级缓存获取,key=AService
        Object singletonObject = this.singletonObjects.get(beanName);
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                // 从二级缓存获取,key=AService 
                singletonObject = this.earlySingletonObjects.get(beanName);
                // 是否允许循环引用
                if (singletonObject == null && allowEarlyReference) {
                   // 前面已经将以Key为AService,value是ObjectFactory类型一个匿名内部类对象放入三级缓存了
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                         //singletonFactory是一个匿名内部类对象,此处触发匿名内部类中getEarlyBeanReference()方法回调。
                        singletonObject = singletonFactory.getObject();
                        // 将三级缓存生产的bean放入二级缓存中
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        // 删除三级缓存
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }

最终三级缓存中查到之前放入的以Key为AService,value为ObjectFactory类型一个匿名内部类对象,从而触发匿名内部类getEarlyBeanReference()方法回调。getEarlyBeanReference()方法决定返回AService实例到底是AService实例本身还是被AOP增强后的AServiceProxy实例对象。如果没AOP切面对AService进行拦截,这时返回的将是AService实例本身。接着将半成品AService Bean放入二级缓存并将Key为AService从三级缓存中删除,这样实现了提前将AService Bean曝光给BService完成属性依赖注入。继续走BService后续初始化逻辑,最后生产了成熟的BService Bean实例。

接着原路返回,AService也成功获取到依赖BService实例,完成后续的初始化工作,然后完美的解决了循环依赖的问题。

5.当AOP遇到循环依赖

从之前动态代理埋点 我们知道Bean的AOP动态代理创建是在初始化bean最后一步postProcessAfterInitialization(),后置处理器进行的,但是出现循环依赖的Bean如果使用了AOP, 那就需要在getEarlyBeanReference()方法创建动态代理,将生成的代理Bean放在二级缓存提前曝光出来, 这样BService的属性aService注入的就是被代理后的AServiceProxy实例对象。


// 将Aservice添加三级缓存
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
// 添加Bservice的aService属性时从三级中找Aservice的ObjectFactory类型一个匿名内部类对象,从而触发匿名内部类getEarlyBeanReference()方法回调,进入创建AService切面代理对象逻辑
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
    Object exposedObject = bean;
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        //判断后置处理器是否实现了SmartInstantiationAwareBeanPostProcessor接口
        //调用SmartInstantiationAwareBeanPostProcessor的getEarlyBeanReference
        for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
            exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
        }
    }
    return exposedObject;
}

可以看出getEarlyBeanReference()方法判断后置处理器是否实现了SmartInstantiationAwareBeanPostProcessor后置处理器接口。

而通过@EnableAspectJAutoProxy注解导入的AOP核心业务处理AnnotationAwareAspectJAutoProxyCreator类,它继承了AbstractAutoProxyCreator了,在AbstractAutoProxyCreator类中实现了getEarlyBeanReference()方法。

//真正实现了该方法的类就是AbstractAutoProxyCreator
public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
      implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware { 
    @Override
    public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
        // 先获取beanName,主要是为FactoryBean类型添加&前缀
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        // 判断是否已经在earlyProxyReferences集合中,不在则添加进去
        if (!this.earlyProxyReferences.contains(cacheKey)) {
            this.earlyProxyReferences.add(cacheKey);
        }
        // 创建代理对象,如果必要的话
        return wrapIfNecessary(bean, beanName, cacheKey);
    }  
    /**
     * Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
     * @param bean the raw bean instance
     * @param beanName the name of the bean
     * @param cacheKey the cache key for metadata access
     * @return a proxy wrapping the bean, or the raw bean instance as-is
     */
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        // 前面先做一些基本的判断
        if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
            return bean;
        }
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        // Advice/Pointcut/Advisor/AopInfrastructureBean接口的beanClass不进行代理以及对beanName为aop内的切面名也不进行代理
        // 此处可查看子类复写的shouldSkip()方法
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }
        // Create proxy if we have advice.
        // 查找对代理类相关的advisor对象集合,此处就与point-cut表达式有关了
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        // 对相应的advisor不为空才采取代理
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            // 通过jdk动态代理或者cglib动态代理,产生代理对象,这里传入的是SingletonTargetSource对象喔,对原始bean对象进行了包装
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            // 放入代理类型缓存
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }
        // 放入通知缓存
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }
}

wrapIfNecessary 方法查找AService是否查找存在的advisor对象集合,此处就与point-cut表达式有关了,显然我们的切点 @Around("execution(* com.example.service.AService.helloA(..))")拦截了AService,因此需要创建AService的代理Bean。通过jdk动态代理或者cglib动态代理,产生代理对象,对原始AService对象进行了包装最后返回的是 AService的代理对象aServiceProxy,然后把 aServiceProxy 放入二级缓存里面,并删除三级缓存中的 AService的ObjectFactory。这样实现了提前为AService生成动态对象aServiceProxy并赋值给BService的aService属性依赖注入。这样BService完成了属性依赖注入,继续走BService后续初始化逻辑,最后生产了成熟的BService Bean实例。当 BService创建完了之后, AService在缓存BService Bean对象完成bService属性注入后,接着走到Bean创建流程的第三步:初始化AService,有上面知识我们知道初始化AService会回调postProcessAfterInitialization后置处理器又开始AOP逻辑。

图片

而此时判断 AService已经存在getEarlyBeanReference()方法中放入earlyProxyReferences了,说明 原始对象已经经历过了AOP,因此就不用重复进行AOP逻辑。

这样AService也完成初始化工作,然后完美的解决了Aservice依赖BService,BService依赖Aservice这个循环依赖的问题。

最后,也来一张解决AService、BService相互依赖,且AService使用了AOP的循环依赖的流程图对上述Spring代码逻辑进行总结。红色部分主要与没有AOP情况AService、BService相互依赖流程区别内容。

图片

五,总结

总结下Spring解决循环依赖的思路:

在创建单例bean时,会把该bean的工厂函数的匿名类对象放入三级缓存中的singletonFactories中;

然后在填充属性时,如果出现循环依赖依赖本 bean,必然执行之前放入的工厂函数的匿名实现,如果该bean无需 AOP的话,工厂函数返回的就是原bean对象;如果该bean有 AOP 的话,也有可能是被某些BBP处理AOP 之后的代理对象,会放入二级缓存中的earlySingletonObjects中;

接着bean开始初始化,如果该bean无需 AOP的话,结果返回的原来创建的bean对象;如果该bean有 AOP 的话,检查AOP织入逻辑是否已经在提前曝光时已经执行了,如果已经执行AOP则返回提前曝光的代理bean对象;如果AOP织入逻辑未执行过,则进行后续的 BeanPostProcessor后置处理器进行AOP织入,生成AOP代理bean对象,并返回。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/886123.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

上山取石&#xff0c;下江取锦。诗人秋浦啸傲&#xff0c;新津樵唱。江南山水秀美&#xff0c;水乡文化流长。而水&#xff0c;则是这些山水风景的灵魂所在。 水&#xff0c;雨露滋润万物生长的泉源。 它潺潺流淌于山间溪涧&#xff0c;涓涓细流化成了青山的眼泪。水顺势而下&a…

Unity如何制作声音控制条(控制音量大小)

一&#xff1a;UGUI制作 1. 首先在【层级】下面创建UI里面的Slider组件。设置好它对应的宽度和高度。 2.调整Slider滑动条的填充颜色。一般声音颜色我黄色&#xff0c;所以我们也调成黄色。 我们尝试滑动Slider里面的value。 a.滑动前。 b.滑动一半。 c.滑动完。 从以上滑动va…

Openai中的tokens怎么估计

大规模语言模型&#xff08;LLM&#xff09;的出现给自然语言处理领域带来了变革的可能性&#xff0c;Openai开放了chatgpt的API&#xff0c;方便了开发人员使用LLM的推理能力&#xff0c;注册时赠送5美元的使用额度&#xff0c;有效期3个月。 如果想便捷的使用chatgpt的API&a…

判断推理

六哥爱学习呀 产品经理 不是说我努力学习我就一定可以通过考试&#xff0c;所以是推不出&#xff0c;类似数学中充分必要性 8 回复 发布于 2019-08-07 16:28 官方解析&#xff1a; 当丙的范围足够大时&#xff0c;可能与甲相交或完全包含甲&#xff0c;在此情况下&#xff0c;有…

【数据结构】顺序队列模拟实现

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

ansible 修改远程主机nginx配置文件

安装ansible brew install ansible 或者 pip3 install ansible 添加远程主机 设置秘钥 mac登录远程主机 ssh -p 5700 root192.168.123.211 ssh localhost #设置双机信任 ssh-kyegen -t rsa #设置主机两边的ssh配置文件 vi /etc/ssh/sshd_config/ PermitRootL…

C++写文件,直接写入结构体

C写文件&#xff0c;直接写入结构体 以前写文件都是写入字符串或者二进制再或者就是一些配置文件&#xff0c;今天介绍一下直接写入结构体&#xff0c;可以在软件参数较多的时候直接进行读写&#xff0c;直接将整个结构体写入和读取&#xff0c;看代码&#xff1a; #include&…

除了ping你用过traceroute吗

一、检查两个计算机间的网络是否通 在检查两个机器的网络通不通&#xff0c;我们经常使用的命令是ping 但是当ping不通时&#xff0c;我们就不知道网络是哪里不通了&#xff0c;只能找网管排查。 这里介绍一个检查网络工具 traceroute 二 、TraceRoute是什么 TraceRoute的中文…

驱动 - 20230816

练习 1.编写LED灯的驱动&#xff0c;可以控制三个灯&#xff0c;应用程序中编写控制灯的逻辑&#xff0c;要使用自动创建设备节点机制 驱动头文件 ledHead.h #ifndef __HEAD_H__ #define __HEAD_H__#define PHY_GPIOE_MODER 0X50006000 #define PHY_GPIOE_ODR 0X50006014 #d…

toB营销如何从品牌营销转向获客营销?

“解构纷享新营销&#xff0c;赋能用户新增长”&#xff0c;这是2023年下半年&#xff0c;纷享销客践行“以客户成功定义成功”价值观&#xff0c;针对企业用户市场营销领域的全国巡回研讨会&#xff0c;希望把纷享销客在成长路上经历的、收获的经验、踩过的“坑”与用户共享&a…

Postman如何做接口测试:什么?postman 还可以做压力测试?

我们都知道&#xff0c; postman 是一款很好用的接口测试工具。不过 postman 还可以做简单的压力测试&#xff0c;而且步骤只需要 2 步。 首先&#xff0c;打开 postman, 编写接口的请求参数。 然后&#xff0c;点击右下方的 runner 运行器&#xff0c;把需要测试的接口拖动到…

序列模型和循环网络

Sequence Modeling and Recurrent Networks Sequence modeling tasks 在以往的模型中&#xff0c;各个输入之间是独立分布的 x ( i ) x^{(i)} x(i) 之间是相互独立的&#xff0c;同样输出 y ( i ) y^{(i)} y(i)之间也是相互独立的。 但是在序列模型中&#xff0c;输入输出是…

应用开源框架平台,实现流程化办公!

如今&#xff0c;实现流程化办公&#xff0c;管理好数据资源是很多企业的共同想法。如果采用传统的办公方式显然无法实现这一愿望。利用开源框架平台&#xff0c;可以管理好数据资源&#xff0c;为企业提高办公协作效率&#xff0c;进入流程化办公。流辰信息是专业的低代码技术…

如何使用Python编写小游戏?

大家好&#xff0c;我是沐尘而生&#xff0c;如果你是一个热爱编程的小伙伴&#xff0c;又想尝试游戏开发&#xff0c;那么这篇文章一定能满足你的好奇心。不废话&#xff0c;让我们马上进入Python游戏开发的精彩世界吧&#xff01; Python游戏开发的魅力 编写小游戏不仅仅是锻…

Linux系统管理:虚拟机ESXi安装

目录 一、理论 1.VMware Workstation 2.VMware vSphere Client 3.ESXi 二、实验 1.ESXi 7安装 一、理论 1.VMware Workstation 它是一款专业的虚拟机软件&#xff0c;可以在一台物理机上运行多个操作系统&#xff0c;支持Windows、Linux等操作系统&#xff0c;可以模拟…

opencv-进阶05 手写数字识别原理及示例

前面我们仅仅取了两个特征维度进行说明。在实际应用中&#xff0c;可能存在着更多特征维度需要计算。 下面以手写数字识别为例进行简单的介绍。 假设我们要让程序识别图 20-2 中上方的数字&#xff08;当然&#xff0c;你一眼就知道是“8”&#xff0c;但是现在要让计算机识别…

lvs负载均衡集群(NAT模式)

lvs负载均衡集群&#xff1a; 1.什么是集群&#xff08;含义&#xff09;&#xff1a;就是将多台主机作为一个整体&#xff0c;对外提供相同的服务 2.集群使用在哪一个场景&#xff1a;高并发 并发量过大时候加服务器的方式就是向外扩展(横向扩展)&#xff0c;就是集群。 3…

HoudiniVex笔记_P24_ForceBasics力基础

原视频&#xff1a;https://www.youtube.com/playlist?listPLzRzqTjuGIDhiXsP0hN3qBxAZ6lkVfGDI Bili&#xff1a;Houdini最强VEX算法教程 - VEX for Algorithmic Design_哔哩哔哩_bilibili Houdini版本&#xff1a;19.5 1、什么是Force 本章主要讲重力、弹力、速度与质量、…

强训第33天

选择 C A ping是TCP/IP协议族的一部分&#xff0c;使用ICMP协议&#xff0c;ICMP底层使用IP协议。如果要ping其他网段&#xff0c;则需要设置网关。 如果是二层交换机故障&#xff0c;则ping同网段的也会不通。 C Dos攻击被称之为“拒绝服务攻击”&#xff0c;其目的是使计算机…

网页设计详解(一)-HTML简介

本文作为博主学习笔记&#xff1a;2023-05-04星期四 一、网页介绍 网页是构成网站的基本元素&#xff0c;它是一个包含HTML标签的纯文本文件&#xff0c;是超文本标记语言格式(文件扩展名为.html或.htm)。网页通常用图像档来提供图画&#xff0c;通过浏览器来阅读。 超文本介…