再談spring的循環(huán)依賴是怎么造成的?
老生常談,循環(huán)依賴!顧名思義嘛,就是你依賴我,我依賴你,然后就造成了循環(huán)依賴了!由于A中注入B,B中注入A導致的嗎?
看起來沒毛病,然而,卻沒有說清楚問題!甚至會讓你覺得你是不清楚spring的循環(huán)依賴的!
那么,spring的循環(huán)依賴到底是啥玩意?
來看個循環(huán)依賴注入失敗的例子:
bean1: userService
@Servicepublic class LoginServiceImpl implements LoginService {// @Resourceprivate UserService userService;public LoginServiceImpl() {// 無參構造器是給默認創(chuàng)建的bean使用的}@Autowiredpublic LoginServiceImpl(UserService userService) {this.userService = userService;}public void setUserService(UserService userService) {this.userService = userService;}public Integer login(UserInfo userInfo) {System.out.println("login...");return 0;}public void logAction(String userId, String action) throws Exception {UserInfo userInfo = userService.getUser(userId);System.out.println("logged..." + userInfo + ",action: " + action);}}
bean2: loginService
@Servicepublic class UserServiceImpl implements UserService {// @Resourceprivate LoginService loginService;public UserServiceImpl() {// 無參構造器是給默認創(chuàng)建的bean使用的}// @Autowiredpublic UserServiceImpl(LoginService loginService) {this.loginService = loginService;}@Autowiredpublic void setLoginService(LoginService loginService) {this.loginService = loginService;}public Integer addUser(UserInfo userInfo) throws Exception {userInfo.setId("111");System.out.println("user added. " + userInfo);// 登錄loginService.login(userInfo);return 1;}public UserInfo getUser(String id) throws Exception {UserInfo userInfo = null;if("111".equals(id)) {userInfo = new UserInfo();userInfo.setId(id);userInfo.setAge(22);userInfo.setName("mocking");return userInfo;}throw new Exception("err:" + ErrorCodeEnum.API_CALL_ERROR.getCode() + "," + ErrorCodeEnum.API_CALL_ERROR.getErrMsg());}}
然后再建一個測試類,來測試以上代碼!
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = {"classpath:applicationContext.xml"})public class UserServiceTest {@Resourceprivate UserService userService;@Testpublic void testAddUserForLoopDI() throws Exception {UserInfo userInfo = new UserInfo();userInfo.setId("12");userInfo.setAddress("sdfds");userService.addUser(userInfo);System.out.println("ok");}}
applicationContext.xml 中只要開啟掃描即可!
<context:annotation-config/><context:component-scan base-package="com.xx"></context:component-scan>
如上例子運行,就可以得到一個循環(huán)依賴的失敗錯誤了!
不過,你不一定能運行進來,因為如例子還要依賴于一個事實,那就 loginService 要在 userService 之前被掃描到,而不同的jvm上,可能spring得到的掃描順序不一致,如果想要100%失敗,則換成兩個類都是構造器注入就可以了!
錯誤堆棧樣例如下:(Is there an unresolvable circular reference?)
java.lang.IllegalStateException: Failed to load ApplicationContextat org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83)at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117)at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:230)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:228)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:287)at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:289)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:247)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)at org.junit.runners.ParentRunner.run(ParentRunner.java:363)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)at org.junit.runner.JUnitCore.run(JUnitCore.java:137)at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'loginService' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'userService' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userService' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'loginService' while setting bean property 'loginService'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'loginService': Requested bean is currently in creation: Is there an unresolvable circular reference?at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:648)at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:145)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1198)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1100)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:511)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:481)at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312)at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308)at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:128)at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60)at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:281)at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:249)at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98)at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116)... 24 moreCaused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userService' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'loginService' while setting bean property 'loginService'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'loginService': Requested bean is currently in creation: Is there an unresolvable circular reference?at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1534)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1281)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:551)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:481)at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312)at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308)at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)... 44 moreCaused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'loginService': Requested bean is currently in creation: Is there an unresolvable circular reference?at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:347)at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308)at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)... 54 morejava.lang.IllegalStateException: Failed to load ApplicationContextat org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83)at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117)at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:230)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:228)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:287)at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:289)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:247)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)at org.junit.runners.ParentRunner.run(ParentRunner.java:363)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)at org.junit.runner.JUnitCore.run(JUnitCore.java:137)at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'loginService' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'userService' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userService' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'loginService' while setting bean property 'loginService'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'loginService': Requested bean is currently in creation: Is there an unresolvable circular reference?at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)at org.springframework.beans.factory.support.ConstructorResolver.resolveConstructorArguments(ConstructorResolver.java:648)at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:145)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1198)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1100)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:511)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:481)at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312)at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308)at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:128)at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60)at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:281)at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:249)at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98)at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116)... 24 moreCaused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userService' defined in class path resource [applicationContext.xml]: Cannot resolve reference to bean 'loginService' while setting bean property 'loginService'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'loginService': Requested bean is currently in creation: Is there an unresolvable circular reference?at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:359)at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:108)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1534)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1281)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:551)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:481)at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:312)at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308)at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)... 44 moreCaused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'loginService': Requested bean is currently in creation: Is there an unresolvable circular reference?at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.beforeSingletonCreation(DefaultSingletonBeanRegistry.java:347)at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:308)at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:351)... 54 more
好了,看完效果,是時候來分析下問題了。
1. 為什么會出現(xiàn)問題?
因為加載流程如下:
1. 首先加載 loginService, 然后構造器被調用,然后發(fā)現(xiàn)依賴了 userService;
2. 依賴注入,先去加載 userService,加載userService后,需要對其屬性進行依賴注入,然后發(fā)現(xiàn)了 loginService 需要被注入;
3. 當去加載 loginService 的時候,發(fā)現(xiàn) loginService 正在創(chuàng)建中,所以這個實例只能認為創(chuàng)建失敗了,否則將會導致更多未知問題;
4. 同理,多個構造器互相注入失敗問題更嚴重;
2. 循環(huán)依賴失敗是不是只要 ABA 就一定會導致失敗?(注:非單例對象一定不會導致循環(huán)依賴)
1. 按正常說是這樣的,但是spring已經(jīng)解決這個問題了;
2. spring 解決方案為,只為單例提供解決方案也只能為單例解決問題;
3. 創(chuàng)建A單例時,放入緩存,然后依賴注入B;
4. 依賴注入B時,發(fā)現(xiàn)需要依賴注入A,然后去加載A,此時從緩存中發(fā)現(xiàn)A正在加載中,于是直接從緩存得到A,完成自身的依賴注入;
5. B依賴完成后,返回給A,A再把B注入到自身域中;
6. B中的A也自然而然的完成了初始化動作;
其中,構造器注入的單例的循環(huán)依賴是無法解決的,因為在構造器注入時,本身的實例無法生成;如果強行使用,將導致不安全的發(fā)布,從而導致各種未知的問題!
3. 源碼如何實現(xiàn)?
1. 首先,我們來看下什么情況下會拋出循環(huán)依賴異常?
// 只有單例的創(chuàng)建會存在循環(huán)依賴問題// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry/** Set of registered singletons, containing the bean names in registration order */private final Set<String> registeredSingletons = new LinkedHashSet<String>(256);/** Names of beans that are currently in creation */private final Set<String> singletonsCurrentlyInCreation =Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));/** Names of beans currently excluded from in creation checks */private final Set<String> inCreationCheckExclusions =Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(16));protected void beforeSingletonCreation(String beanName) {if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}}
從上面的代碼可以看出,拋出循環(huán)依賴問題會拋出有兩種前提:
1. 沒有包含在創(chuàng)建檢查的排除列表中;
2. 沒有被排除,則檢查是不是第一次被調用創(chuàng)建,如果單例不是第一次被調用創(chuàng)建,則不能再創(chuàng)建了(否則就不是單例了);
所以,避免循環(huán)依賴有個出口,那就是提前把單例放到檢查排除列表中!
那么什么時候會被加入到創(chuàng)建檢查排除列表?
public void setCurrentlyInCreation(String beanName, boolean inCreation) {Assert.notNull(beanName, "Bean name must not be null");if (!inCreation) {this.inCreationCheckExclusions.add(beanName);}else {this.inCreationCheckExclusions.remove(beanName);}}
具體如何設置呢?其實普通的使用不會用到這個功能,只會在一些增強點作這些工作。

所以,spring 如何避免循環(huán)依賴失敗?
// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean()/*** Actually create the specified bean. Pre-creation processing has already happened* at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.* <p>Differentiates between default bean instantiation, use of a* factory method, and autowiring a constructor.* @param beanName the name of the bean* @param mbd the merged bean definition for the bean* @param args explicit arguments to use for constructor or factory method invocation* @return a new instance of the bean* @throws BeanCreationException if the bean could not be created* @see #instantiateBean* @see #instantiateUsingFactoryMethod* @see #autowireConstructor*/protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)throws BeanCreationException {// 第一步,創(chuàng)建bean實例// Instantiate the bean.BeanWrapper instanceWrapper = null;if (mbd.isSingleton()) {instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);}if (instanceWrapper == null) {instanceWrapper = createBeanInstance(beanName, mbd, args);}final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);mbd.resolvedTargetType = beanType;// Allow post-processors to modify the merged bean definition.synchronized (mbd.postProcessingLock) {if (!mbd.postProcessed) {try {applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);}catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Post-processing of merged bean definition failed", ex);}mbd.postProcessed = true;}}// 對于提前暴露的bean, 將它加入單例緩存中,從而使后續(xù)的依賴調用時,可以將此實例返回// Eagerly cache singletons to be able to resolve circular references// even when triggered by lifecycle interfaces like BeanFactoryAware.boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));if (earlySingletonExposure) {if (logger.isDebugEnabled()) {logger.debug("Eagerly caching bean '" + beanName +"' to allow for resolving potential circular references");}// 針對提前暴露的 singleton, 添加一個 獲取實例 工廠,以備后續(xù)獲取實例時使用addSingletonFactory(beanName, new ObjectFactory<Object>() {@Overridepublic Object getObject() throws BeansException {return getEarlyBeanReference(beanName, mbd, bean);}});}// 第二步,初始化實例,完成依賴注入等工作// Initialize the bean instance.Object exposedObject = bean;try {populateBean(beanName, mbd, instanceWrapper);if (exposedObject != null) {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);if (earlySingletonReference != null) {if (exposedObject == bean) {exposedObject = earlySingletonReference;}else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {String[] dependentBeans = getDependentBeans(beanName);Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);for (String dependentBean : dependentBeans) {if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {actualDependentBeans.add(dependentBean);}}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.");}}}}// 第三步,對外暴露bean,如 singleton 入 容器中注冊實例// Register bean as disposable.try {registerDisposableBeanIfNecessary(beanName, bean, mbd);}catch (BeanDefinitionValidationException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);}return exposedObject;}
而在獲取 singleton 時,會先嘗試從工廠中獲取!
// org.springframework.beans.factory.support.DefaultSingletonBeanRegistryprotected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName);// 針對正在創(chuàng)建中的 singleton, 將其放入 earlySingletonObjectsif (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {synchronized (this.singletonObjects) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);if (singletonFactory != null) {// 將 singleton 實例從工廠中取出后,存入 提前暴露的緩存中,交將創(chuàng)建工廠刪除,避免后續(xù)反復從中獲取 singletonsingletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}return (singletonObject != NULL_OBJECT ? singletonObject : null);}// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getEarlyBeanReference()protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {Object exposedObject = bean;if (bean != null && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);if (exposedObject == null) {return null;}}}}return exposedObject;}
在獲取bean實例時,會先嘗試從緩存中直接獲取,失敗再進行真實的創(chuàng)建:
// org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean()// 會先嘗試從單例緩存中獲取bean,獲取不到再進行創(chuàng)建/*** Return an instance, which may be shared or independent, of the specified bean.* @param name the name of the bean to retrieve* @param requiredType the required type of the bean to retrieve* @param args arguments to use when creating a bean instance using explicit arguments* (only applied when creating a new instance as opposed to retrieving an existing one)* @param typeCheckOnly whether the instance is obtained for a type check,* not for actual use* @return an instance of the bean* @throws BeansException if the bean could not be created*/@SuppressWarnings("unchecked")protected <T> T doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)throws BeansException {final String beanName = transformedBeanName(name);Object bean;// 先從緩存獲取,失敗則進行創(chuàng)建// Eagerly check singleton cache for manually registered singletons.Object sharedInstance = getSingleton(beanName);if (sharedInstance != null && args == null) {if (logger.isDebugEnabled()) {if (isSingletonCurrentlyInCreation(beanName)) {logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +"' that is not fully initialized yet - a consequence of a circular reference");}else {logger.debug("Returning cached instance of singleton bean '" + beanName + "'");}}bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);}else {// Fail if we're already creating this bean instance:// We're assumably within a circular reference.if (isPrototypeCurrentlyInCreation(beanName)) {throw new BeanCurrentlyInCreationException(beanName);}// Check if bean definition exists in this factory.BeanFactory parentBeanFactory = getParentBeanFactory();if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {// Not found -> check parent.String nameToLookup = originalBeanName(name);if (args != null) {// Delegation to parent with explicit args.return (T) parentBeanFactory.getBean(nameToLookup, args);}else {// No args -> delegate to standard getBean method.return parentBeanFactory.getBean(nameToLookup, requiredType);}}if (!typeCheckOnly) {markBeanAsCreated(beanName);}try {final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);checkMergedBeanDefinition(mbd, beanName, args);// Guarantee initialization of beans that the current bean depends on.String[] dependsOn = mbd.getDependsOn();if (dependsOn != null) {for (String dep : dependsOn) {if (isDependent(beanName, dep)) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");}registerDependentBean(dep, beanName);getBean(dep);}}// Create bean instance.if (mbd.isSingleton()) {// 此時根據(jù)對象工廠,創(chuàng)建bean,創(chuàng)建時檢測依賴情況sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {@Overridepublic Object getObject() throws BeansException {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);}else if (mbd.isPrototype()) {// It's a prototype -> create a new instance.Object prototypeInstance = null;try {beforePrototypeCreation(beanName);prototypeInstance = createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);}else {String scopeName = mbd.getScope();final Scope scope = this.scopes.get(scopeName);if (scope == null) {throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");}try {Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {@Overridepublic Object getObject() throws BeansException {beforePrototypeCreation(beanName);try {return createBean(beanName, mbd, args);}finally {afterPrototypeCreation(beanName);}}});bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);}catch (IllegalStateException ex) {throw new BeanCreationException(beanName,"Scope '" + scopeName + "' is not active for the current thread; consider " +"defining a scoped proxy for this bean if you intend to refer to it from a singleton",ex);}}}catch (BeansException ex) {cleanupAfterBeanCreationFailure(beanName);throw ex;}}// Check if required type matches the type of the actual bean instance.if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {try {return getTypeConverter().convertIfNecessary(bean, requiredType);}catch (TypeMismatchException ex) {if (logger.isDebugEnabled()) {logger.debug("Failed to convert bean '" + name + "' to required type '" +ClassUtils.getQualifiedName(requiredType) + "'", ex);}throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());}}return (T) bean;}// org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton()// 進行 singleton 創(chuàng)建時,做安全檢查,成功后放入緩存中/*** Return the (raw) singleton object registered under the given name,* creating and registering a new one if none registered yet.* @param beanName the name of the bean* @param singletonFactory the ObjectFactory to lazily create the singleton* with, if necessary* @return the registered singleton object*/public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(beanName, "'beanName' must not be null");synchronized (this.singletonObjects) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {if (this.singletonsCurrentlyInDestruction) {throw new BeanCreationNotAllowedException(beanName,"Singleton bean creation not allowed while singletons of this factory are in destruction " +"(Do not request a bean from a BeanFactory in a destroy method implementation!)");}if (logger.isDebugEnabled()) {logger.debug("Creating shared instance of singleton bean '" + beanName + "'");}// 創(chuàng)建前檢查,循環(huán)依賴失敗在此時檢測到,即單例只能被創(chuàng)建一次beforeSingletonCreation(beanName);boolean newSingleton = false;boolean recordSuppressedExceptions = (this.suppressedExceptions == null);if (recordSuppressedExceptions) {this.suppressedExceptions = new LinkedHashSet<Exception>();}try {// createBean()singletonObject = singletonFactory.getObject();newSingleton = true;}catch (IllegalStateException ex) {// Has the singleton object implicitly appeared in the meantime ->// if yes, proceed with it since the exception indicates that state.singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {throw ex;}}catch (BeanCreationException ex) {if (recordSuppressedExceptions) {for (Exception suppressedException : this.suppressedExceptions) {ex.addRelatedCause(suppressedException);}}throw ex;}finally {if (recordSuppressedExceptions) {this.suppressedExceptions = null;}// 最后,清除正在創(chuàng)建的標識afterSingletonCreation(beanName);}// 單例創(chuàng)建成功,則將其加入到緩存中,并清理之前的標記if (newSingleton) {addSingleton(beanName, singletonObject);}}return (singletonObject != NULL_OBJECT ? singletonObject : null);}}/*** Add the given singleton object to the singleton cache of this factory.* <p>To be called for eager registration of singletons.* @param beanName the name of the bean* @param singletonObject the singleton object*/protected void addSingleton(String beanName, Object singletonObject) {synchronized (this.singletonObjects) {this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));this.singletonFactories.remove(beanName);this.earlySingletonObjects.remove(beanName);this.registeredSingletons.add(beanName);}}
如上代碼,說明了bean的創(chuàng)建過程,我們主要看 singleton。
1. 嘗試從緩存中獲取bean,可能是提前暴露的,也可能是已經(jīng)完全初始化好的;(提前暴露解決循環(huán)依賴)
2. 如果獲取不到,則進行一次完整的初始化,通過 對象工廠的 getObject() 進行對象創(chuàng)建;
3. 創(chuàng)建前,先將正在創(chuàng)建中標識寫入,從而防止后續(xù)又有進行創(chuàng)建動作;(拋出創(chuàng)建異常)
4. 創(chuàng)建好 singleton 后,將多余標識刪除,并將 singleton 放入緩存,以待下次直接使用;
最后,我們用一個示意圖描述下整個過程:

思考題:
依賴注入使代碼更簡單和靈活,可以自動查找關聯(lián)關系!當發(fā)現(xiàn)有依賴時,相當于遞歸生成里層bean實例!而每個bean的創(chuàng)建都經(jīng)過n層方法的調用;
那么請問,在創(chuàng)建bean時有沒有可能導致棧溢出??

騰訊、阿里、滴滴后臺面試題匯總總結 — (含答案)
面試:史上最全多線程面試題 !
最新阿里內推Java后端面試題
JVM難學?那是因為你沒認真看完這篇文章

關注作者微信公眾號 —《JAVA爛豬皮》
了解更多java后端架構知識以及最新面試寶典


看完本文記得給作者點贊+在看哦~~~大家的支持,是作者源源不斷出文的動力
作者:等你歸去來
出處:https://www.cnblogs.com/yougewe/p/10146537.html
