首页后端开发其他后端知识Java循环依赖是什么意思,怎么样解决循环依赖?

Java循环依赖是什么意思,怎么样解决循环依赖?

时间2024-03-28 20:48:03发布访客分类其他后端知识浏览375
导读:Java循环依赖是什么意思?对于Java循环依赖有很多朋友不知道什么东西,所谓循环依赖就是A依赖B,同时B又依赖A,两者之间的依赖关系形成了一个圆环,一般是由于不正确的编码所导致。因此我们需要解决Java循环依赖的问题,具体怎样做呢? S...

Java循环依赖是什么意思?对于Java循环依赖有很多朋友不知道什么东西,所谓循环依赖就是A依赖B,同时B又依赖A,两者之间的依赖关系形成了一个圆环,一般是由于不正确的编码所导致。因此我们需要解决Java循环依赖的问题,具体怎样做呢?

Spring只能解决属性循环依赖问题,不能解决构造函数循环依赖问题,因为这个问题无解。

接下来我们首先写一个Demo来演示Spring是如何处理属性循环依赖问题的。

Talk is cheap. Show me the code

第一步:定义一个类ComponentA,其有一个私有属性componentB。

package com.tech.ioc;
    

import org.springframework.beans.factory.annotation.Autowired;
    
import org.springframework.stereotype.Component;


/**
 * @author 君战
 * **/
@Component
public class ComponentA {
    

	@Autowired
	private ComponentB componentB;


	public void say(){
    
		componentB.say();

	}


}
    

第二步:定义一个类ComponentB,其依赖ComponentA。并定义一个say方法便于打印数据。

package com.tech.ioc;
    

import org.springframework.beans.factory.annotation.Autowired;
    
import org.springframework.stereotype.Component;

/**
 * @author 君战
 * **/
@Component
public class ComponentB {
    

	@Autowired
	private ComponentA componentA;


	public void say(){
    
		System.out.println("componentA field " + componentA);
    
		System.out.println(this.getClass().getName() + " ----->
     say()");

	}


}
    

第三步:重点,编写一个类-SimpleContainer,模仿Spring底层处理循环依赖。如果理解这个代码,再去看Spring处理循环依赖的逻辑就会很简单。

package com.tech.ioc;
    

import java.beans.Introspector;
    
import java.lang.reflect.Constructor;
    
import java.lang.reflect.Field;
    
import java.util.HashMap;
    
import java.util.Map;
    
import java.util.concurrent.ConcurrentHashMap;


/**
 * 演示Spring中循环依赖是如何处理的,只是个简版,真实的Spring依赖处理远比这个复杂。
 * 但大体思路都相同。另外这个Demo很多情况都未考虑,例如线程安全问题,仅供参考。
 * @author 君战
 *
 * **/
public class SimpleContainer {
    

	/***
	 * 用于存放完全初始化好的Bean,Bean处于就绪状态
	 * 这个Map定义和Spring中一级缓存命名一致
	 * */
	private MapString, Object>
     singletonObjects = new ConcurrentHashMap>
    ();
    

	/***
	 * 用于存放刚创建出来的Bean,其属性还没有处理,因此存放在该缓存中的Bean还不可用。
	 * 这个Map定义和Spring中三级缓存命名一致
	 * */
	private final MapString, Object>
     singletonFactories = new HashMap>
    (16);



	public static void main(String[] args) {
    
		SimpleContainer container = new SimpleContainer();
    
		ComponentA componentA = container.getBean(ComponentA.class);
    
		componentA.say();

	}
    

	public T>
     T getBean(ClassT>
 beanClass) {
    
		String beanName = this.getBeanName(beanClass);
    
		// 首先根据beanName从缓存中获取Bean实例
		Object bean = this.getSingleton(beanName);

		if (bean == null) {
    
			// 如果未获取到Bean实例,则创建Bean实例
			return createBean(beanClass, beanName);

		}
    
		return (T) bean;

	}

	/***
	 * 从一级缓存和二级缓存中根据beanName来获取Bean实例,可能为空
	 * */
	private Object getSingleton(String beanName) {
    
		// 首先尝试从一级缓存中获取
		Object instance = singletonObjects.get(beanName);

		if (instance == null) {
     // Spring 之所以能解决循环依赖问题,也是靠着这个三级缓存--singletonFactories
			instance = singletonFactories.get(beanName);

		}
    
		return instance;

	}
    

	/***
	 * 创建指定Class的实例,返回完全状态的Bean(属性可用)
	 *
	 * */
	private T>
     T createBean(ClassT>
 beanClass, String beanName) {

		try {
    
			ConstructorT>
     constructor = beanClass.getDeclaredConstructor();
    
			T instance = constructor.newInstance();
    
			// 先将刚创建好的实例存放到三级缓存中,如果没有这一步,Spring 也无法解决三级缓存
			singletonFactories.put(beanName, instance);
    
			Field[] fields = beanClass.getDeclaredFields();

			for (Field field : fields) {
    
				Class?>
     fieldType = field.getType();
    
				field.setAccessible(true);
     
				// 精髓是这里又调用了getBean方法,例如正在处理ComponentA.componentB属性,
				// 执行到这里时就会去实例化ComponentB。因为在getBean方法首先去查缓存,
				// 而一级缓存和三级缓存中没有ComponentB实例数据,所以又会调用到当前方法,
				// 而在处理ComponentB.componentA属性时,又去调用getBean方法去缓存中查找,
				// 因为在前面我们将ComponentA实例放入到了三级缓存,因此可以找到。
				// 所以ComponentB的实例化结束,方法出栈,返回到实例化ComponentA的方法栈中,
				// 这时ComponentB已经初始化完成,因此ComponentA.componentB属性赋值成功!
				field.set(instance, this.getBean(fieldType));

			}
    
			// 最后再将初始化好的Bean设置到一级缓存中。
			singletonObjects.put(beanName, instance);
    
			return instance;

		}
 catch (Exception e) {
    
			e.printStackTrace();

		}
    
		throw new IllegalArgumentException();

	}


	/**
	 * 将类名小写作为beanName,Spring底层实现和这个差不多,也是使用javaBeans的
	 * {
@linkplain Introspector#decapitalize(String)}
    
	 **/
	private String getBeanName(Class?>
 clazz) {
    
		String clazzName = clazz.getName();
    
		int index = clazzName.lastIndexOf(".");
    
		String className = clazzName.substring(index);
    
		return Introspector.decapitalize(className);

	}

}
    

如果各位同学已经阅读并理解上面的代码,那么接下来我们就进行真实的Spring处理循环依赖问题源码分析,相信再阅读起来就会很容易。

底层源码分析

分析从AbstractBeanFactory的doGetBean方法着手。可以看到在该方法首先调用transformedBeanName(其实就是处理BeanName问题),和我们自己写的getBeanName方法作用是一样的,但Spring考虑的远比这个复杂,因为有FactoryBean、别名问题。

// AbstractBeanFactory#doGetBean
protected T>
     T doGetBean(
			String name, @Nullable ClassT>
 requiredType, @Nullable Object[] args, boolean typeCheckOnly)
			throws BeansException {
    

		String beanName = transformedBeanName(name);
    
		Object bean;
    

		// !!!重点是这里,首先从缓存中beanName来获取对应的Bean。
		Object sharedInstance = getSingleton(beanName);
    
		if (sharedInstance != null &
    &
 args == null) {
    
// 执行到这里说明缓存中存在指定beanName的Bean实例,getObjectForBeanInstance是用来处理获取到的Bean是FactoryBean问题
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);

		else {

			try {

				// 删除与本次分析无关代码....
				// 如果是单例Bean,则通过调用createBean方法进行创建
				if (mbd.isSingleton()) {
    
					sharedInstance = getSingleton(beanName, () ->
 {

						try {
    
							return createBean(beanName, mbd, args);

						}
 catch (BeansException ex) {
    
							destroySingleton(beanName);
    
							throw ex;

						}

					}
    );

				
				}
	
			catch (BeansException ex) {
    
				cleanupAfterBeanCreationFailure(beanName);
    
				throw ex;

			}

		}
    
		return (T) bean;

	}

getSingleton方法存在重载方法,这里调用的是重载的getSingleton方法,注意这里传递的boolean参数值为true,因为该值决定了是否允许曝光早期Bean。

// DefaultSingletonBeanRegistry#getSingleton
public Object getSingleton(String beanName) {
    
	return getSingleton(beanName, true);

}
// DefaultSingletonBeanRegistry#getSingleton
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
    
		// 首先从一级缓存中获取
		Object singletonObject = this.singletonObjects.get(beanName);
    
		if (singletonObject == null &
    &
 isSingletonCurrentlyInCreation(beanName)) {
    
			// 如果一级缓存中未获取到,再从二级缓存中获取
			singletonObject = this.earlySingletonObjects.get(beanName);
    
			// 如果未从二级缓存中获取到并且allowEarlyReference值为true(前面传的为true)
			if (singletonObject == null &
    &
 allowEarlyReference) {

				synchronized (this.singletonObjects) {
    
				   //Double Check 
					singletonObject = this.singletonObjects.get(beanName);

					if (singletonObject == null) {
    
						singletonObject = this.earlySingletonObjects.get(beanName);

						if (singletonObject == null) {
    
							// 最后尝试去三级缓存中获取
				ObjectFactory?>
     singletonFactory = this.singletonFactories.get(beanName);

							if (singletonFactory != null) {
    
								singletonObject = singletonFactory.getObject();
    
								// 保存到二级缓存
								this.earlySingletonObjects.put(beanName, singletonObject);
    
								// 从三级缓存中移除
								this.singletonFactories.remove(beanName);

							}

						}

					}

				}

			}

		}
    
		return singletonObject;

	}

ok,看完Spring是如何从缓存中获取Bean实例后,那再看看creatBean方法是如何创建Bean的

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

	// 删除与本次分析无关的代码...
	try {
    // createBean方法底层是通过调用doCreateBean来完成Bean创建的。
		Object beanInstance = doCreateBean(beanName, mbdToUse, args);

		if (logger.isTraceEnabled()) {
    
			logger.trace("Finished creating instance of bean '" + beanName + "'");

		}
    
		return beanInstance;

	}
 catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
    
		throw ex;

	}
 catch (Throwable ex) {
    
		throw new BeanCreationException(
		mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);

	}

}
// AbstractAutowireCapableBeanFactory#doCreateBean
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {
    

		BeanWrapper instanceWrapper = null;

		if (mbd.isSingleton()) {
    
			instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);

		}

		if (instanceWrapper == null) {
    
			// 创建Bean实例
			instanceWrapper = createBeanInstance(beanName, mbd, args);

		}
    
		Object bean = instanceWrapper.getWrappedInstance();
    
		// 如果允许当前Bean早期曝光。只要Bean是单例的并且allowCircularReferences 属性为true(默认为true)
		boolean earlySingletonExposure = (mbd.isSingleton() &
    &
     this.allowCircularReferences &
    &
    
				isSingletonCurrentlyInCreation(beanName));

		if (earlySingletonExposure) {
    
			// 这里调用了addSingletonFactory方法将刚创建好的Bean保存到了三级缓存中。
			addSingletonFactory(beanName, () ->
     getEarlyBeanReference(beanName, mbd, bean));

		}
    

		// 删除与本次分析无关的代码.....
		Object exposedObject = bean;

		try {
    // Bean属性填充
			populateBean(beanName, mbd, instanceWrapper);
    
			// 初始化Bean,熟知的Aware接口、InitializingBean接口.....都是在这里调用
			exposedObject = initializeBean(beanName, exposedObject, mbd);

		}
 catch (Throwable ex) {

			
		}
    
		// 删除与本次分析无关的代码.....
		return exposedObject;

	}
    

先分析addSingletonFactory方法,因为在该方法中将Bean保存到了三级缓存中。

protected void addSingletonFactory(String beanName, ObjectFactory?>
 singletonFactory) {
    
	Assert.notNull(singletonFactory, "Singleton factory must not be null");

	synchronized (this.singletonObjects) {

		// 如果一级缓存中不存在指定beanName的key
		if (!this.singletonObjects.containsKey(beanName)) {
    
			// 将刚创建好的Bean实例保存到三级缓存中
			this.singletonFactories.put(beanName, singletonFactory);
    
			// 从二级缓存中移除。
			this.earlySingletonObjects.remove(beanName);
    
			this.registeredSingletons.add(beanName);

		}

	}

}

处理Bean的依赖注入是由populateBean方法完成的,但整个执行链路太长了,这里就不展开讲了,只说下IoC容器在处理依赖时是如何一步一步调用到getBean方法的,这样就和我们自己写的处理字段注入的逻辑对上了。

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    
	// 删除与本次分析无关代码...
	PropertyDescriptor[] filteredPds = null;

	if (hasInstAwareBpps) {

		if (pvs == null) {
    
			pvs = mbd.getPropertyValues();

		}

// 遍历所有已注册的BeanPostProcessor接口实现类,
//如果实现类是InstantiationAwareBeanPostProcessor接口类型的,调用其postProcessProperties方法。
		for (BeanPostProcessor bp : getBeanPostProcessors()) {

			if (bp instanceof InstantiationAwareBeanPostProcessor) {
    
				InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
    
				PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
    
				// 删除与本次分析无关代码...
				pvs = pvsToUse;

			}

		}

		// 删除与本次分析无关代码...
	}

	
}

在Spring 中,@Autowired注解是由AutowiredAnnotationBeanPostProcessor类处理,而@Resource注解是由CommonAnnotationBeanPostProcessor类处理,这两个类都实现了InstantiationAwareBeanPostProcessor接口,都是在覆写的postProcessProperties方法中完成了依赖注入。这里我们就分析@Autowired注解的处理。

// AutowiredAnnotationBeanPostProcessor#postProcessProperties
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    
		// 根据beanName以及bean的class去查找Bean的依赖元数据-InjectionMetadata 
		InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);

		try {
    // 调用inject方法
			metadata.inject(bean, beanName, pvs);

		}
 catch (BeanCreationException ex) {
    
			throw ex;

		}
 catch (Throwable ex) {
    
			throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);

		}
    
		return pvs;

	}

在InjectionMetadata的inject方法中,获取当前Bean所有需要处理的依赖元素(InjectedElement),这是一个集合,遍历该集合,调用每一个依赖注入元素的inject方法。

// InjectionMetadata#inject
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    
	// 获取当前Bean所有的依赖注入元素(可能是方法,也可能是字段)
	CollectionInjectedElement>
     checkedElements = this.checkedElements;
    
	CollectionInjectedElement>
     elementsToIterate =
			(checkedElements != null ? checkedElements : this.injectedElements);

	if (!elementsToIterate.isEmpty()) {

		// 如果当前Bean的依赖注入项不为空,遍历该依赖注入元素
		for (InjectedElement element : elementsToIterate) {
    
			// 调用每一个依赖注入元素的inject方法。
			element.inject(target, beanName, pvs);

		}

	}

}

在AutowiredAnnotationBeanPostProcessor类中定义了两个内部类-AutowiredFieldElement、AutowiredMethodElement继承自InjectedElement,它们分别对应字段注入和方法注入。

以大家常用的字段注入为例,在AutowiredFieldElement的inject方法中,首先判断当前字段是否已经被处理过,如果已经被处理过直接走缓存,否则调用BeanFactory的resolveDependency方法来处理依赖。

// AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    
		Field field = (Field) this.member;
    
		Object value;

		if (this.cached) {
    // 如果当前字段已经被处理过,直接从缓存中获取
			value = resolvedCachedArgument(beanName, this.cachedFieldValue);

		}
 else {
    
			// 构建依赖描述符
			DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
    
			desc.setContainingClass(bean.getClass());
    
			SetString>
     autowiredBeanNames = new LinkedHashSet>
    (1);
    
			Assert.state(beanFactory != null, "No BeanFactory available");
    
			TypeConverter typeConverter = beanFactory.getTypeConverter();

			try {
    // 调用BeanFactory的resolveDependency来解析依赖
				value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);

			}
 catch (BeansException ex) {
    
				throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);

			}

			// 删除与本次分析无关代码....
		}

		if (value != null) {
    
			// 通过反射来对属性进行赋值
			ReflectionUtils.makeAccessible(field);
    
			field.set(bean, value);

		}

	}

}
    

在DefaultListableBeanFactory实现的resolveDependency方法,最终还是调用doResolveDependency方法来完成依赖解析的功能。在Spring源码中,如果存在do什么什么方法,那么该方法才是真正干活的方法。

// DefaultListableBeanFactory#resolveDependency
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
		@Nullable SetString>
 autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    
		// .....
		// 如果在字段(方法)上添加了@Lazy注解,那么在这里将不会真正的去解析依赖
		Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
				descriptor, requestingBeanName);

		if (result == null) {
    
			// 如果未添加@Lazy注解,那么则调用doResolveDependency方法来解析依赖
			result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);

		}
    
		return result;

}
    
// DefaultListableBeanFactory#doResolveDependency
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
		@Nullable SetString>
 autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {


	//.....
	try {
    
		// 根据名称以及类型查找合适的依赖
		MapString, Object>
     matchingBeans = findAutowireCandidates(beanName, type, descriptor);

		if (matchingBeans.isEmpty()) {
// 如果未找到相关依赖
			if (isRequired(descriptor)) {
     // 如果该依赖是必须的(例如@Autowired的required属性),直接抛出异常
				raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);

			}
    
			return null;

		}
    

		String autowiredBeanName;
    
		Object instanceCandidate;
    
		// 如果查找到的依赖多于一个,例如某个接口存在多个实现类,并且多个实现类都注册到IoC容器中。
		if (matchingBeans.size() >
 1) {
    // 决定使用哪一个实现类,@Primary等方式都是在这里完成
			autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);

			if (autowiredBeanName == null) {

				if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
    
					return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);

				}
 else {
     
					return null;

				}

			}
    
			instanceCandidate = matchingBeans.get(autowiredBeanName);

		}
 else {
    
			// We have exactly one match.
			Map.EntryString, Object>
     entry = matchingBeans.entrySet().iterator().next();
    
			autowiredBeanName = entry.getKey();
    
			instanceCandidate = entry.getValue();

		}


		if (autowiredBeanNames != null) {
    
			autowiredBeanNames.add(autowiredBeanName);

		}

		// 如果查找到的依赖是某个类的Class(通常如此),而不是实例,
		//调用描述符的方法来根据类型resolveCandidate方法来获取该类型的实例。
		if (instanceCandidate instanceof Class) {
    
			instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);

		}

		//...
}
    

在依赖描述符的resolveCandidate方法中,是通过调用BeanFactory 的getBean方法来完成所依赖Bean实例的获取。

// DependencyDescriptor#resolveCandidate
public Object resolveCandidate(String beanName, Class?>
 requiredType, BeanFactory beanFactory)
			throws BeansException {
    

	return beanFactory.getBean(beanName);

}

而在getBean方法实现中,依然是通过调用doGetBean方法来完成。这也和我们自己写的依赖处理基本一致,只不过我们自己写的比较简单,而Spring要考虑和处理的场景复杂,因此代码比较繁杂,但大体思路都是一样的。

// AbstractBeanFactory#getBean
public Object getBean(String name) throws BeansException {
    
	return doGetBean(name, null, null, false);

}
    

重点是前面我们写的处理循环依赖的Demo,如果理解那个代码,再看Spring的循环依赖处理,就会发现很简单。

总结:

循环依赖就是指两个Bean之间存在相互引用关系,例如A依赖B,B又依赖A,但Spring只能解决属性循环依赖,不能解决构造函数循环依赖,这种场景也无法解决。

Spring解决循环依赖的关键就是在处理Bean的属性依赖时,先将Bean存到三级缓存中,当存在循环依赖时,从三级缓存中获取到相关Bean,然后从三级缓存中移除,存入到二级缓存中,最后初始化完毕后存入到一级缓存中。

关于Java循环依赖及解决方法就介绍到这,上述方法具有一定的参考价值,感兴趣的朋友可以了解看看,希望能对大家有帮助,想要了解更多Java循环依赖的内容,大家可以关注网络其它的相关文章。

文本转载自PHP中文网

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!


若转载请注明出处: Java循环依赖是什么意思,怎么样解决循环依赖?
本文地址: https://pptw.com/jishu/655189.html
Java怎么样连接mysql,都有哪些方式? Java的特性包括哪些,怎样理解?

游客 回复需填写必要信息