首先向大家介绍下什么是循环依赖。
(学习视频分享:java视频教程)
所谓循环依赖就是A依赖B,同时B又依赖A,两者之间的依赖关系形成了一个圆环,一般是由于不正确的编码所导致。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 君战 * **/@Componentpublic class ComponentA {@Autowiredprivate 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 君战 * **/@Componentpublic class ComponentB {@Autowiredprivate 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 Map<String, Object> singletonObjects = new ConcurrentHashMap<>();/*** * 用于存放刚创建出来的Bean,其属性还没有处理,因此存放在该缓存中的Bean还不可用。 * 这个Map定义和Spring中三级缓存命名一致 * */private final Map<String, 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(Class<T> 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 之所以能解决循环依赖问题,也是靠着这个三级缓存--singletonFactoriesinstance = singletonFactories.get(beanName);}return instance;}/*** * 创建指定Class的实例,返回完全状态的Bean(属性可用) * * */private <T> T createBean(Class<T> beanClass, String beanName) {try {Constructor<T> 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#doGetBeanprotected <T> T doGetBean(String name, @Nullable Class<T> 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#getSingletonpublic Object getSingleton(String beanName) {return getSingleton(beanName, true);}
// DefaultSingletonBeanRegistry#getSingletonprotected 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;}
(更多面试题请访问:java面试题及答案)
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#doCreateBeanprotected 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的keyif (!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#postProcessPropertiespublic 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#injectpublic void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {// 获取当前Bean所有的依赖注入元素(可能是方法,也可能是字段)Collection<InjectedElement> checkedElements = this.checkedElements;Collection<InjectedElement> 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#injectprotected 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());Set<String> 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#resolveDependencypublic Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,@Nullable Set<String> 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#doResolveDependencypublic Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {//.....try {// 根据名称以及类型查找合适的依赖Map<String, 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.Entry<String, 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#resolveCandidatepublic Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory)throws BeansException {return beanFactory.getBean(beanName);}
而在getBean方法实现中,依然是通过调用doGetBean方法来完成。这也和我们自己写的依赖处理基本一致,只不过我们自己写的比较简单,而Spring要考虑和处理的场景复杂,因此代码比较繁杂,但大体思路都是一样的。
// AbstractBeanFactory#getBeanpublic 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入门教程