spring bean循环依赖应该是spring 源码中比较难的一块知识了,下面我们结合代码还有时序图,来进行分析,看下spring 是如何优雅的处理spring bean的循环依赖的。
我们都知道spring的IOC 和DI,它可以帮助我们创建对象,并且可以帮我们自动注入需要spring管理的对象。然后会存在一种这样的情况,在对象 A 中需要依赖 B,而在对象 B 中有又依赖 A,当然可以有更多的对象依赖,他们之间会组成了一个闭环,如下图
在spring 初始化bean A的时候发现bean A依赖于bean B,然后去实例化bean B,实例化bean B的时候又发现bean B依赖于bean A…,这样陷入了一个无限循环的过程中,最终可能会导致内存溢出。当然,这只是我们的一个设想,spring当然不会让这样的事情发生。spring提供一个很优雅的方式来帮我们解决了bean之间循环依赖的问题,通过引入三级缓存机制来解决。
另外再说一点,不是所有的循环依赖spring 都可以解决,以下两种方式spring也是无法解决的
在正式开始之前,我们再说下spring的三级缓存以及定义
先来看下代码中是如何定义的
/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name to ObjectFactory. */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name to bean instance. */
private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
下面我们结合代码来做进一步的分析
创建类BeanA,然后通过set注入BeanB
public class BeanA {
private BeanB beanB;
public void setBeanB(BeanB beanB) {
this.beanB = beanB;
}
}
创建类BeanB,然后通过set注入BeanA
public class BeanB {
private BeanA beanA;
public void setBeanA(BeanA beanA) {
this.beanA = beanA;
}
}
将BeanA 和BeanB 交给spring来进行管理
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="beanA" class="com.wtf.test.BeanA">
<property name="beanB" ref="beanB"/>
bean>
<bean id="beanB" class="com.wtf.test.BeanB">
<property name="beanA" ref="beanA"/>
bean>
beans>
使用ClassPathXmlApplicationContext
来初始化bean
public class BeanTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
Object beanA = applicationContext.getBean("beanA");
System.out.println(beanA);
}
}
在分析源码前我们先来看下spring 循环依赖的时序图,对于后面我们分析源码会有很大的帮助
原始文件可以从这里下载
我们调用了ClassPathXmlApplicationContext的带参构造方法,最终调用了下面的构造。这里完成了三件事:
super(parent)
初始化父类setConfigLocations(configLocations)
设置本地的配置信息refresh()
完成spring容器的初始化这里我们重点观察第三个方法,因为spring bean的初始化是在这里完成的
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
// 初始化父类
super(parent);
// 设置本地的配置信息
setConfigLocations(configLocations);
// 完成Spring容器的初始化
if (refresh) {
refresh();
}
}
上面说了,refresh方法是初始化spring容器的,所以这是一个核心方法。这里面包含beanFactory 和bean的初始化操作,因为方法比较多,对每个方法都进行了注释,不一一赘述了,这里我们重点关注finishBeanFactoryInitialization()
,这个方法做了一下操作
public void refresh() throws BeansException, IllegalStateException {
// 对象锁加锁
synchronized (this.startupShutdownMonitor) {
//刷新前的预处理,表示在真正做refresh操作之前需要准备做的事情:
prepareRefresh();
/*
获取BeanFactory;默认实现是DefaultListableBeanFactory
加载BeanDefition 并注册到 BeanDefitionRegistry
*/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//BeanFactory的预准备工作(BeanFactory进行一些设置,比如context的类加载器等)
prepareBeanFactory(beanFactory);
try {
//BeanFactory准备工作完成后进行的后置处理工作
postProcessBeanFactory(beanFactory);
//实例化实现了BeanFactoryPostProcessor接口的Bean,并调用接口方法
invokeBeanFactoryPostProcessors(beanFactory);
//注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执行
registerBeanPostProcessors(beanFactory);
//初始化MessageSource组件(做国际化功能;消息绑定,消息解析);
initMessageSource();
//初始化事件派发器
initApplicationEventMulticaster();
//子类重写这个方法,在容器刷新的时候可以自定义逻辑;如创建Tomcat,Jetty等WEB服务器
onRefresh();
//注册应用的监听器。就是注册实现了ApplicationListener接口的监听器bean
registerListeners();
/* Instantiate all remaining (non-lazy-init) singletons.
初始化所有剩下的非懒加载的单例bean
初始化创建非懒加载方式的单例Bean实例(未设置属性)
填充属性
初始化方法调用(比如调用afterPropertiesSet方法、init-method方法)
调用BeanPostProcessor(后置处理器)对实例bean进行后置处理
*/
finishBeanFactoryInitialization(beanFactory);
//完成context的刷新。主要是调用LifecycleProcessor的onRefresh()方法,并且发布事件(ContextRefreshedEvent)
finishRefresh();
} catch (BeansException ex) {
destroyBeans();
cancelRefresh(ex);
throw ex;
} finally {
resetCommonCaches();
}
}
}
这个方法的最后调用了beanFactory的preInstantiateSingletons()
方法,接下继续跟踪preInstantiateSingletons()
方法
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
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));
}
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}
// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);
// Allow for caching all bean definition metadata, not expecting further changes.
beanFactory.freezeConfiguration();
// Instantiate all remaining (non-lazy-init) singletons.
// 实例化所有立即加载的单例bean
beanFactory.preInstantiateSingletons();
}
preInstantiateSingletons()
中获取了所有需要spring管理的bean的name,然后需要遍历,进行bean的创建,核心方法是getBean(beanName)
,根据获取到的bean name进行bean的初始化,所以接下来我们跟踪getBean(beanName)
public void preInstantiateSingletons() throws BeansException {
if (logger.isTraceEnabled()) {
logger.trace("Pre-instantiating singletons in " + this);
}
// 获取所有bean的名字
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// 触发所有非延迟加载单例bean的初始化,主要步骤为getBean
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
// 如果是FactoryBean则加&
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
// 实例化当前bean
getBean(beanName);
}
}
}
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();
}
}
}
}
为了方便阅读删除了部分源码
上面方法中调用了getBean()
,getBean()
又调用了doGetBean()
,经过前面的铺垫,其实到这里,才开始了真正的循环依赖相关的源码逻辑。
之前我们说过spring 的三级缓存,这里根据bean name先调用getSingleton(beanName)
方法,getSingleton(beanName)
方法中又调用了它的重载方法。我们先假设要去找的bean为beanA,spring先从一级缓存中去找我们要的beanA,找不到的话去二级缓存中去找,二级缓存还找不到去一级缓存中去拿,当然,因为刚加载,所以三级缓存中也是没有的。所以代码继续往下执行。
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// 解析beanName 如果以&开头去掉&开头,如果是别名获取到真正的名字
final String beanName = transformedBeanName(name);
// 尝试从一二三级缓存中获取 bean
Object sharedInstance = getSingleton(beanName);
// 如果已经存在则返回
if (sharedInstance != null && args == null) {
// 针对 FactoryBean 的处理
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {
// 如果是prototype类型且开启允许循环依赖,则抛出异常
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName)
// 创建单例bean
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
// 创建 bean
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
}
}
return (T) bean;
}
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
//先从一级缓存singletonObjects中拿
Object singletonObject = this.singletonObjects.get(beanName);
// isSingletonCurrentlyInCreation(beanName)判断当前单例bean是否正在创建中
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
//一级缓存没有从二级缓存earlySingletonObjects中去拿
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
//二级缓存没有从三级缓存singletonFactories去拿
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject();
//三级缓存中有的话放入二级缓存中,同时从一级缓存中删除
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
接着上面的doGetBean()
方法往下走,又调用了getSingleton()
的另一个重载方法,可以看到第二个参数是一个ObjectFactory接口,而这里spring采用了一个lambda表达式来实现了getObject()方法。调用传进来的lambda表达式singletonFactory.getObject(),进行bean的创建。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name 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 + "'");
}
// 验证完要真正开始创建对象,先标识该bean正在被创建,因为spingbean创建过程复杂,步骤很多,需要标识
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
// 传进来的调用,lamda表达式使用
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;
}
afterSingletonCreation(beanName);
}
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
接下来看下lambda表达式中的createBean(beanName, mbd, args)
方法是如何实现的
createBean()
中其实又调用了doCreateBean()
,它才是真正干活的。这里首先调用对createBeanInstance(beanName, mbd, args)
对beanA进行实例化,然后放入三级缓存中,接下来调用populateBean(beanName, mbd, instanceWrapper)
对beanA进行属性填充,在我们这里其实就是填充beanB,我们继续看填充方法中做了什么
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
// 拿到Bd
RootBeanDefinition mbdToUse = mbd;
//....
try {
// 进入,真真正正创建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;
}
}
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 创建 Bean 实例,仅仅调用构造方法,但是尚未设置属性
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
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));
}
//...
Object exposedObject = bean;
try {
// Bean属性填充
populateBean(beanName, mbd, instanceWrapper);
// 调用初始化方法,应用BeanPostProcessor后置处理器
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
//...
return exposedObject;
}
populateBean()
中最后调用了applyPropertyValues(beanName, mbd, bw, pvs)
,applyPropertyValues(beanName, mbd, bw, pvs)
方法中有调用了resolveValueIfNecessary(pv, originalValue)
,resolveValueIfNecessary(pv, originalValue)
中又调用了resolveReference(argName, ref)
,好了终于到重要关头了,在resolveReference(argName, ref)
中我们可用看到,又调用了getBean(refName)
方法,获取beanB。呀,这是什么情况,怎么又回到起点了?哈哈,别着急,我们继续分析。
既然调用了getBean()
方法,我们不妨在回头看看(代码我就不再贴一遍了)。
getBean()
继续调用doGetBean()
方法,然后继续调用getSingleton()
方法去缓存中拿beanB,然后发现beanB也不再缓存中,然后就开始去创建beanB。创建beanB先进行实例化,然后放入到缓存中,之后再调用populateBean()
进行属性填充(其实就是填充依赖beanA),之前我们提到beanA,在实例化beanB之前已经实例化完成并放入到三级缓存中了,这里我们就可以使用了,虽然还是个娃娃,但是够用了。到这里beanB属性填充之后就可以长大,成为完成的bean了。这时候将beanB放入到缓存池中,我们就可以回过头来填充beanA的属性了。到这里 ,beanA、beanB就完成了他们整个bean的创建过程了。
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
//...
if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
//...
for (PropertyValue pv : original) {
if (pv.isConverted()) {
deepCopy.add(pv);
} else {
String propertyName = pv.getName();
Object originalValue = pv.getValue();
//
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
Object convertedValue = resolvedValue;
//...
}
}
//...
}
public Object resolveValueIfNecessary(Object argName, @Nullable Object value) {
if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference ref = (RuntimeBeanReference) value;
return resolveReference(argName, ref);
}
//...
}
private Object resolveReference(Object argName, RuntimeBeanReference ref) {
try {
Object bean;
String refName = ref.getBeanName();
refName = String.valueOf(doEvaluate(refName));
if (ref.isToParent()) {
bean = this.beanFactory.getParentBeanFactory().getBean(refName);
}
else {
bean = this.beanFactory.getBean(refName);
this.beanFactory.registerDependentBean(refName, this.beanName);
}
if (bean instanceof NullBean) {
bean = null;
}
return bean;
}
catch (BeansException ex) {
}
}
小提示:单纯的看下面的源码可能会比较枯燥,我们可用结合上面的时序图一起来看,会发现清晰很多,另外建议自己创建一个工程,然后开始调试模式一步步跟踪,你会发现更容易理解。