首先向大家介紹下什麼是循環依賴。
(學習影片分享: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 君战 * **/ @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 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 之所以能解决循环依赖问题,也是靠着这个三级缓存--singletonFactories instance = 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#doGetBean protected <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#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; }
(更多面試題請造訪: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#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介面,都是在覆寫方法的了依賴注入。這裡我們就分析@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所有的依赖注入元素(可能是方法,也可能是字段) 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#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()); 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#resolveDependency public 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#doResolveDependency public 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#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面試題:你知道什麼是循環依賴麼? Spring是如何解決循環依賴的?的詳細內容。更多資訊請關注PHP中文網其他相關文章!