spring-ioc

ioc

  • 初始化容器阶段
  • 初始化beanfactory
  • 解析配置成beanDefine
  • 注册bd到bf中
  • 执行BeanFactoryPostProcessor
  • 根据bd针对单例非懒加载bean进行实例化
  • 实例化会执行bfProcessor和BeanProcessor的钩子函数
执行 BeanFactoryPostProcessor ...
执行 BeanPostProcessor-postProcessBeforeInitialization... beanName:dowJonesNewsListener
执行 BeanPostProcessor-postProcessAfterInitialization... beanName:dowJonesNewsListener
执行 BeanPostProcessor-postProcessBeforeInitialization... beanName:djNewsProvider
执行 BeanPostProcessor-postProcessAfterInitialization... beanName:djNewsProvider
执行 BeanPostProcessor-postProcessBeforeInitialization... beanName:dowJonesNewsPersister
执行 BeanPostProcessor-postProcessAfterInitialization... beanName:dowJonesNewsPersister
执行 BeanFactoryAware ...
执行 BeanPostProcessor-postProcessBeforeInitialization... beanName:ioc.spring.BeanAwareDemo#0
执行 BeanPostProcessor-postProcessAfterInitialization... beanName:ioc.spring.BeanAwareDemo#0
执行 业务方法。。。。。
123

FactoryBean 的作用主要用于一些复杂对象的初始化,比如数据库连接池

源码解读

public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext();
        applicationContext.setAllowCircularReferences(true);
        applicationContext.setConfigLocation("spring-beans.xml");
        applicationContext.refresh();
        FXNewsProvider newsProvider = (FXNewsProvider) applicationContext.getBean("djNewsProvider");
        newsProvider.getAndPersistNews();
    }

核心方法是refresh

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// 记录容器的启动时间、标记“已启动”状态
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
            //解析配置文件成BeanDefine map集合,创建BeanFactory并存储BeanDefine,此处并未进行bean实例化
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 配置classLoader,设置PostProcessor,设置一些ignore依赖接口(不自动注入)
			prepareBeanFactory(beanFactory);

			try {
				// 非重点
				postProcessBeanFactory(beanFactory);

				// 调用配置的BFP,配置的所有BFP都会在这个地方被执行
				invokeBeanFactoryPostProcessors(beanFactory);

				// 注册配置的BP到容器中,注意不会执行BP,BP只会在Bean实例化前后被调用
				registerBeanPostProcessors(beanFactory);

				// 初始化MessageSouce到容器中(系统国际化会用到)
				initMessageSource();

				// 初始化applicationEventMulticaster到容器中
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
                //模版方法,可以被重写,主要进行其他特殊bean的初始化操作
				onRefresh();

				// 注册listener  暂过
				registerListeners();

				// 初始化剩下的所有非懒加载的单例模式对象,重点
				finishBeanFactoryInitialization(beanFactory);

				// 最后一步,发布refresh完成事件,主要是调用配置的lifecycleProcessor
				finishRefresh();
			}
		}
	}

spring配置文件会有几个比较特殊bean,对应上面的几个方法,都是从map中取出注册(初始化)到容器中

  • processor
  • aware
  • messageSource
  • applicationEventMulticaster
  • lifecycleProcessor
  • conversionService

核心方法 finishBeanFactoryInitialization

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
		// 初始化conversionService,这个用途可以是进行传输类型转化,比如常用的前端传日期类型到后端,可以使用他进行处理
		if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
				beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
			beanFactory.setConversionService(
					beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
		}
......

		// 防止配置被修改,进行冻结
		beanFactory.freezeConfiguration();

		// 重点是这个,接着深入
		beanFactory.preInstantiateSingletons();
	}

DefaultListableBeanFactory . preInstantiateSingletons

public void preInstantiateSingletons() throws BeansException {
		if (logger.isTraceEnabled()) {
			logger.trace("Pre-instantiating singletons in " + this);
		}
 ......       

		if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {

        //AccessController.doPrivileged是特权模式,如果系统设置安全验证,会对包含的方法进行权限判断,没权限会抛出异常
		isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
											((SmartFactoryBean<?>) factory)::isEagerInit,
									getAccessControlContext());
	
......    

		else {
            // 关键方法 对应到 doGetBean
			getBean(beanName);
		}
......

		// 针对单例bean 如果实现SmartInitializingSingleton,会执行回调函数afterSingletonsInstantiated
		for (String beanName : beanNames) {
			Object singletonInstance = getSingleton(beanName);
			if (singletonInstance instanceof SmartInitializingSingleton) {
				final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
				if (System.getSecurityManager() != null) {
					AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
						smartSingleton.afterSingletonsInstantiated();
						return null;
					}, getAccessControlContext());
				} else {
					smartSingleton.afterSingletonsInstantiated();
				}
			}
		}
	}

AbstractBeanFactory . doGetBean 这个类十分的长,可能是spring为了做到通用,搞得很复杂

              ......
				// 初始化依赖bean
				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);
						try {
                            //递归调用自身
							getBean(dep);
						}
						catch (NoSuchBeanDefinitionException ex) {
							throw new BeanCreationException(mbd.getResourceDescription(), beanName,
									"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
						}
					}
				}
               .....
				// 重要!!!!
				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);
				}

				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);
				}
                ......

AbstractAutowireCapableBeanFactory . createBean

这部分代码比较多,简述一下吧。 主要是采用Java反射(支持CGLIB)进行类的实例化。 在实例化完成以后执行相应的BFP和Aware及init方法,destroy方法 关键代码如下

......
		if (instanceWrapper == null) {
            //这里实例化 Bean,这里非常关键
			instanceWrapper = createBeanInstance(beanName, mbd, args);
		}

......
      try {
            // 负责属性装配,因为前面的实例只是实例化了,并没有设值,这里就是设值
			populateBean(beanName, mbd, instanceWrapper);
            //还记得 init-method 吗?还有 InitializingBean 接口?还有 BeanPostProcessor 接口?
            // 这里就是处理 bean 初始化完成后的各种回调
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}  

需要注意的是 BFP和想象的不太一样,它的before和after方法都是在实例化完成后执行。 理解设计这个的目的主要针对init方法来说的,init方法是在before和after中间执行的。

AbstractAutowireCapableBeanFactory . initializeBean

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
   if (System.getSecurityManager() != null) {
      AccessController.doPrivileged(new PrivilegedAction<Object>() {
         @Override
         public Object run() {
            invokeAwareMethods(beanName, bean);
            return null;
         }
      }, getAccessControlContext());
   }
   else {
      // 如果 bean 实现了 BeanNameAware、BeanClassLoaderAware 或 BeanFactoryAware 接口,回调
      invokeAwareMethods(beanName, bean);
   }

   Object wrappedBean = bean;
   if (mbd == null || !mbd.isSynthetic()) {
      // BeanPostProcessor 的 postProcessBeforeInitialization 回调
      wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
   }

   try {
      // 处理 bean 中定义的 init-method,
      // 或者如果 bean 实现了 InitializingBean 接口,调用 afterPropertiesSet() 方法
      invokeInitMethods(beanName, wrappedBean, mbd);
   }
   catch (Throwable ex) {
      throw new BeanCreationException(
            (mbd != null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
   }

   if (mbd == null || !mbd.isSynthetic()) {
      // BeanPostProcessor 的 postProcessAfterInitialization 回调
      wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
   }
   return wrappedBean;
}

spring循环引用问题

场景1 (单例对象构造方式注入)
    <bean name="djNewsProvider" class="ioc.spring.FXNewsProvider">
		<constructor-arg name="newsListener" ref="dowJonesNewsListener"/>
	</bean>
	<bean name="dowJonesNewsListener" class="ioc.spring.DowJonesNewsListener">
		<constructor-arg name="fxNewsProvider" ref="djNewsProvider"/>
	</bean>

默认情况下支持循环引用,可通过setAllowCircularReferences修改

    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext();
        // applicationContext.setAllowCircularReferences(false);
        applicationContext.setConfigLocation("spring-beans.xml");
        applicationContext.refresh();
        FXNewsProvider newsProvider = (FXNewsProvider) applicationContext.getBean("djNewsProvider");
        newsProvider.getAndPersistNews();
    }

抛出BeanCurrentlyInCreationException

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'djNewsProvider' defined 
in class path resource [spring-beans.xml]: Cannot resolve reference to bean 'dowJonesNewsListener' while setting 
bean property 'newsListener'; nested exception is **org.springframework.beans.factory.BeanCreationException**: Error 
creating bean with name 'dowJonesNewsListener' defined in class path resource [spring-beans.xml]: Cannot resolve 
reference to bean 'djNewsProvider' while setting bean property 'fxNewsProvider'; nested exception is **org.springframework.beans.factory.BeanCurrentlyInCreationException**: Error creating bean 
with name 'djNewsProvider': Requested bean is currently in creation: Is there an unresolvable circular reference?
场景2 (单例对象set方式注入)
    <bean name="djNewsProvider" class="ioc.spring.FXNewsProvider">
		<property name="newsListener" ref="dowJonesNewsListener"/>
	</bean>
	<bean name="dowJonesNewsListener" class="ioc.spring.DowJonesNewsListener">
		<property name="fxNewsProvider" ref="djNewsProvider"/>
	</bean>

默认情况下支持循环引用,可通过setAllowCircularReferences修改

    public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext();
        applicationContext.setAllowCircularReferences(false);
        applicationContext.setConfigLocation("spring-beans.xml");
        applicationContext.refresh();
        FXNewsProvider newsProvider = (FXNewsProvider) applicationContext.getBean("djNewsProvider");
        newsProvider.getAndPersistNews();
    }

抛出BeanCurrentlyInCreationException

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'djNewsProvider' defined 
in class path resource [spring-beans.xml]: Cannot resolve reference to bean 'dowJonesNewsListener' while setting 
bean property 'newsListener'; nested exception is **org.springframework.beans.factory.BeanCreationException**: Error 
creating bean with name 'dowJonesNewsListener' defined in class path resource [spring-beans.xml]: Cannot resolve 
reference to bean 'djNewsProvider' while setting bean property 'fxNewsProvider'; nested exception is **org.springframework.beans.factory.BeanCurrentlyInCreationException**: Error creating bean 
with name 'djNewsProvider': Requested bean is currently in creation: Is there an unresolvable circular reference?
场景3 (多例对象set方式注入)
 <bean name="djNewsProvider" class="ioc.spring.FXNewsProvider" scope="prototype">
		<property name="newsListener" ref="dowJonesNewsListener"/>
	</bean>
	<bean name="dowJonesNewsListener" class="ioc.spring.DowJonesNewsListener" scope="prototype">
		<property name="fxNewsProvider" ref="djNewsProvider"/>
	</bean>

抛出BeanCurrentlyInCreationException

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'djNewsProvider' defined in class path resource [spring-beans.xml]: 
Cannot resolve reference to bean 'dowJonesNewsListener' while setting bean property 'newsListener'; 
nested exception is org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'dowJonesNewsListener' defined in class path resource [spring-beans.xml]: 
Cannot resolve reference to bean 'djNewsProvider' while setting bean property 'fxNewsProvider'; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: 
Error creating bean with name 'djNewsProvider': Requested bean is currently in creation: Is there an unresolvable circular reference?
	at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:342)


spring是如何解决循环依赖?

spring 只支持单例对象使用set属性方式注入循环依赖,构造函数注入以及多例对象是不支持的。单例对象在执行完构造函数之后,在未设置属性之前,会将实例化完成的对象缓存到early缓存中,每次zai

AbstractAutowireCapableBeanFactory.doCreateBean

		// Eagerly cache singletons to be able to resolve circular references
		// even when triggered by lifecycle interfaces like BeanFactoryAware.

		//注意看注释部分,Eagerly(early?官方注释错误) cache singletons to be able to resolve circular references  循环依赖主要通过ealy缓存解决
		//allowCircularReferences 默认为true,允许循环依赖,可以配置
		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");
			}
			addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
		}

        // 和想象的一样,early缓存发生在实例化完成之后,属性设置之前。
		Object exposedObject = bean;
		try {
			populateBean(beanName, mbd, instanceWrapper);
			exposedObject = initializeBean(beanName, exposedObject, mbd);
		}

DefaultSingletonBeanRegistry.getSingleton 利用三级缓存

  • singletonObjects
  • earlySingletonObjects
  • singletonFactories

先从so中取,取不到 判断是否是正在创建的单例bean,不是的话直接返回NULL,如果是正创建bean, 从earlySingletonObjects中取,此处表示,正创建的bean,实例化完成会加到early中,如果还取不到, 就从singletonFactories中取,如果取到了 则将结果放到early中,并从factory中移除。

protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		Object singletonObject = this.singletonObjects.get(beanName);
		if (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) {
						singletonObject = singletonFactory.getObject();
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName);
					}
				}
			}
		}
		return singletonObject;
	}

下面分析抛出BeanCurrentlyInCreationException的源头

多例对象直接抛出

			// Fail if we're already creating this bean instance:
			// We're assumably within a circular reference.
			if (isPrototypeCurrentlyInCreation(beanName)) {
				throw new BeanCurrentlyInCreationException(beanName);
			}

DefaultSingletonBeanRegistry.getSingleton

......
beforeSingletonCreation(beanName);
......
afterSingletonCreation(beanName);
......

DefaultSingletonBeanRegistry.beforeSingletonCreation DefaultSingletonBeanRegistry.afterSingletonCreation

......
/** 当前正在创建的bean名称集合 
存储的主要是正在实例化的bean

*/
	private final Set<String> singletonsCurrentlyInCreation =
			Collections.newSetFromMap(new ConcurrentHashMap<>(16));


//每次创建单例bean之前都会检查
	protected void beforeSingletonCreation(String beanName) {
		//关键代码this.singletonsCurrentlyInCreation.add(beanName) 如果已经存在正创建集合中 会返回false,这时候会抛出上面所说的循环引用异常
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}

protected void afterSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
			throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
		}
	}
......

spring循环依赖三层缓存 AbstractBeanFactory.doGetBean

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
......
		// Eagerly check singleton cache for manually registered singletons.
		Object sharedInstance = getSingleton(beanName);
		if (sharedInstance != null && args == null) {
......			

参考