前言
Spring已经成为了开发项目的不可缺少的组件了,我们在平常开发项目中难免会遇到以下这些情况,比如说,我有A类和B类,两个业务类都注入到Spring容器里了,且双方都互相注入了,这个时候就会造成循环依赖的问题,相信之前有很多开发者遇到这样的问题吧,不过现在Spring底层已经通过三级缓存来解决了这个循环依赖的问题了。
什么是循环依赖
循环依赖通俗来讲,Bean与Bean之间相互依赖了,或者说多个Bean之间相互依赖,最终形成闭环操作(闭环可以理解为死循环的意思)
如下图所演示
A实例依赖B实例,B实例依赖C实例,C实例依赖A实例,就这样他们最终形成了一个循环依赖的关系
需要注意是的是:
上图指的循环依赖不是方法之间的调用,而是对象之间的相互引用
Spring Bean的循环依赖
谈到Spring Bean循环依赖,估计大家伙可能遇到的比较少,毕竟在开发的过程中好像没有循环依赖这个概念,或者换个说法说,在开发的coding阶段,没有关于循环依赖的报错等
Spring Bean循环依赖的几种场景
在Sping环境中,我们的Bean的实例化以及初始化都是交给了Spring容器进行管理,因此为了演示哈,我这里建立了3个类。
1.通过构造器进行注入
@Component
public class InstanceAService {
public InstanceAService(InstanceBService instanceBService) {
}
}
@Component
public class InstanceBService {
public InstanceBService(InstanceCService instanceBService) {
}
}
@Component
public class InstanceCService {
public InstanceCService(InstanceAService instanceAService) {
}
}
结果:======
SpringBoot报错信息如下:
┌─────┐
| instanceAService defined in file [D:\learn\Learning project\springboot_prometheus\target\classes\com\dream\sunny\InstanceAService.class]
↑ ↓
| instanceBService defined in file [D:\learn\Learning project\springboot_prometheus\target\classes\com\dream\sunny\InstanceBService.class]
↑ ↓
| instanceCService defined in file [D:\learn\Learning project\springboot_prometheus\target\classes\com\dream\sunny\InstanceCService.class]
└─────┘
Spring原生报错信息如下:
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'instanceAService' defined in file [C:\Users\sunny\Downloads\Compressed\spring-framework-5.2.10.RELEASE\spring-sunny-ioc\build\classes\java\main\com\dream\sunny\customer\xunhuan\InstanceAService.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'instanceBService' defined in file [C:\Users\sunny\Downloads\Compressed\spring-framework-5.2.10.RELEASE\spring-sunny-ioc\build\classes\java\main\com\dream\sunny\customer\xunhuan\InstanceBService.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'instanceCService' defined in file [C:\Users\sunny\Downloads\Compressed\spring-framework-5.2.10.RELEASE\spring-sunny-ioc\build\classes\java\main\com\dream\sunny\customer\xunhuan\InstanceCService.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'instanceAService': Requested bean is currently in creation: Is there an unresolvable circular reference?
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:797)
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:227)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1416)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1263)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:571)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:531)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:414)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:288)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:411)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:239)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:939)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:946)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:600)
at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:119)
at com.dream.sunny.customer.xunhuan.InstanceStart.main(InstanceStart.java:14)
通过如上案例,通过构造器方式注入的Bean,Spring底层是无法解决循环依赖的问题,所以只能抛出异常:BeanCurrentlyInCreationException
1.1通过构造器注入的解决方案
@Component
public class InstanceAService {
public InstanceAService(@Lazy InstanceBService instanceBService) {
}
}
@Component
public class InstanceBService {
public InstanceBService(InstanceCService instanceBService) {
}
}
@Component
public class InstanceCService {
public InstanceCService(InstanceAService instanceAService) {
}
}
结果:======
程序正常启动
如上案例,消除循环依赖的方式是通过延迟加载,只需要在任意一个构造注入中使用@Lazy即可解决
2.通过Setter注入
这种方式注入是我们最常用的一种依赖注入方式(Spring底层通过三级缓存解决循环依赖)
@Component
public class InstanceAService {
@Autowired
private InstanceBService instanceBService;
}
@Component
public class InstanceBService {
@Autowired
private InstanceCService instanceCService;
}
@Component
public class InstanceCService {
@Autowired
private InstanceAService instanceAService;
}
结果:======
程序正常启动
Spring容器三级缓存
三级缓存其实更像Spring容器的术语,采用三级缓存来解决循环依赖问题,三级缓存的大概作用如下:
名称 | 描述 |
---|---|
singletonObjects | 一级缓存,存放完整的Bean |
earlySingletonObjects | 二级缓存,存放提前暴露的Bean,且该Bean是不完整的 |
singletonFactories | 三级缓存,存放的是Bean工厂,主要生产Bean的 |
从源码方面来分析Spring的循环依赖
提示:本文的分析重点主要在于Spring的循环依赖部分,其他的部分本文则会一笔带过,如对其他部分感兴趣的伙伴们,可参考作者该系列其他文章。
本文的循环依赖源码分析的开始前提是基于setter注入方式分析哦
老规矩,我们还是通过注解配置类的方式作为入口,我们进入
AnnotationConfigApplicationContext的构造方法
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(InstanceConfig.class);
进入后,我们看到构造方法里面的有三个方法,这个方法我们不着重讲解
1.this():该方法会初始化BeanFactory容器,完成Spring内部的后置处理器解析成BeanDefinition,读取和扫描我们的配置类等
2.register();该方法会将扫描到的配置类注册成Bean定义
3.refresh();Spring的生命周期的所有操作都在该方法中,也是Spring的核心所在
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
//调用构造函数,this():调用本身构造函数,但是如果有父类的话,会先走父类的构造函数,再走子类的构造函数
this();
//注册配置类(将配置类注册到Bean定义中)
register(componentClasses);
//IOC容器刷新接口
refresh();
}
好,大概了解完后,我们直接进入refresh()方法,该方法内部有很多方法,我这里忽略掉了部分Spring源码
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
...
// 调用我们的bean工厂的后置处理器. 1. 会在此将class扫描成beanDefinition 2.bean工厂的后置处理器调用
//BeanFactoryPostProcessors (解析配置类)
invokeBeanFactoryPostProcessors(beanFactory);
// 注册我们bean的后置处理器
registerBeanPostProcessors(beanFactory);
// 初始化国际化资源处理器.
initMessageSource();
// 创建事件多播器
initApplicationEventMulticaster();
// 这个方法同样也是留个子类实现的springboot也是从这个方法进行启动tomcat的.
onRefresh();
//把我们的事件监听器注册到多播器上
registerListeners();
// 实例化剩余的单实例Bean
finishBeanFactoryInitialization(beanFactory);
...
}
}
我们前面讲到过,通过AnnotationConfigApplicationContext()的构造方法,会将Spring内部的类注册成Bean定义和扫描到的配置类注册到Bean定义中,从下图中我们可以看到,beanDefinitionNames集合中存在多个内容,其中一个则是我们的配置类哈,其他的部分则是Spring内部的后置处理器
接着我们看到invokeBeanFactoryPostProcessors()方法,该方法会将扫描到类注册成Bean定义,这里指的扫描的类是在该类中加了@Component或者@Bean等注解
我们看到下图,执行了该方法后,会将加了@Component的类解析的Bean定义进行存储
该方法很深,不是本文的重点,我们就不继续往下跟了。我们只需要知道该方法的主要作用就是将我们的类解析成Bean定义
回到上面,然后看到我们finishBeanFactoryInitialization()方法,该方法会去实例化单例Bean
我们跟入进去,该方法里的内部方法都有注释,这里就不做分析,我们只关注重点
看到beanFactory.preInstantiateSingletons();该方法就是去实例化所有的单例Bean,而且是非懒加载的Bean
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// 为我们的bean工厂创建类型转化器 Convert
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之前没有注册嵌入值解析器,则注册默认的嵌入值解析器:主要用于注解属性值的解析。
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}
// 初始化LoadTimeWeaverAware Bean实例对象
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);
//允许缓存所有bean定义元数据,而不期望进一步的更改
//冻结所有的Bean定义,说明注册的Bean定义将不被修改或任何进一步处理(可以理解为:Spring要进行生产Bean了,所以不允许外部再进行修改Bean)
beanFactory.freezeConfiguration();
//实例化所有的单例Bean,且非Lazy加载的Bean
beanFactory.preInstantiateSingletons();
}
我们跟进去beanFactory.preInstantiateSingletons();方法
@Override
public void preInstantiateSingletons() throws BeansException {
...
//获取我们容器中所有bean定义的名称
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// Trigger initialization of all non-lazy singleton beans...
//循环我们所有的bean定义名称
for (String beanName : beanNames) {
//合并我们的bean定义,转换为统一的RootBeanDefinition类型(在), 方便后续处理
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
/**
* 根据bean定义判断是不是抽象的&& 不是单例的 &&不是懒加载的
*/
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
//是否为工厂Bean
if (isFactoryBean(beanName)) {
// 是factoryBean会先生成实际的bean &beanName 是用来获取实际bean的
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
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());
}
//调用真正的getBean的流程
if (isEagerInit) {
getBean(beanName);
}
}
} else {
//非工厂Bean,就是普通的Bean对象
getBean(beanName);
}
}
}
...
}
通过图加代码更好理解
我们进入方法,首先会获取到所有Bean定义放入到集合中,并且会逐个去遍历它,我们这里为了方便,直接条件断点,直接进入条件断点
然后看到我Debug的第一行,我们的Bean会有以不同的类型注册的,这里为了方便统一类型,Spring通过getMergedLocalBeanDefinition(beanName); 方法,将我们注册的Bean统一成RootBeanDefinition
接着会进行判断当前遍历的Bean定义,如果该Bean不是抽象且是单例且非懒加载才会进入,当然我这里都是满足的
然后又会接着判断,当前Bean是否是工厂Bean,当然我们的Bean并没有实现FactoryBean接口,并不是工厂Bean,所以会进行else,去实例化Bean对象
关于工厂Bena的详细介绍,伙伴们可以自行百度哈,这里不做多解释
好,那我们进入getBean()方法,在该方法内又调用了doGetBean()方法,众所周知在Spring源码里面,带了do的方法才是真正干活的方法
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
我们再接着进入到doGetBean()方法,好,重头戏就从这里开始了
protected <T> T doGetBean(
String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly)
throws BeansException {
/**
* 在这里 传入进来的name 可能是别名, 也有可能是工厂bean的name,所以在这里需要转换,获取最真实的Bean名称
*/
String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
//尝试去缓存中获取对象(从一级缓存获取数据)
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
} else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
/**
* /*
*
* 如果 sharedInstance 是普通的单例 bean,下面的方法会直接返回。但如果
* sharedInstance 是 FactoryBean 类型的,则需调用 getObject 工厂方法获取真正的
* bean 实例。如果用户想获取 FactoryBean 本身,这里也不会做特别的处理,直接返回
* 即可。毕竟 FactoryBean 的实现类本身也是一种 bean,只不过具有一点特殊的功能而已。
*/
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
/**
* spring 只能解决单例对象的setter 注入的循环依赖,不能解决构造器注入
*/
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
/**
* 判断AbstractBeanFacotry工厂是否有父工厂(一般情况下是没有父工厂因为abstractBeanFactory直接是抽象类,不存在父工厂)
* 一般情况下,只有Spring 和SpringMvc整合的时才会有父子容器的概念,
* 比如我们的Controller中注入Service的时候,发现我们依赖的是一个引用对象,那么他就会调用getBean去把service找出来
* 但是当前所在的容器是web子容器,那么就会在这里的 先去父容器找
*/
BeanFactory parentBeanFactory = getParentBeanFactory();
//若存在父工厂,切当前的bean工厂不存在当前的bean定义,那么bean定义是存在于父beanFacotry中
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
//获取bean的原始名称
String nameToLookup = originalBeanName(name);
//若为 AbstractBeanFactory 类型,委托父类处理
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
// 委托给构造函数 getBean() 处理
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if (requiredType != null) {
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
// 没有 args,委托给标准的 getBean() 处理
return (T) parentBeanFactory.getBean(nameToLookup);
}
}
/**
* 方法参数 typeCheckOnly ,是用来判断调用 #getBean(...) 方法时,表示是否为仅仅进行类型检查获取 Bean 对象
* 如果不是仅仅做类型检查,而是创建 Bean 对象,则需要调用 #markBeanAsCreated(String beanName) 方法,进行记录
*/
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
/**
* 从容器中获取 beanName 相应的 GenericBeanDefinition 对象,并将其转换为 RootBeanDefinition 对象
* <bean id="tulingParentCompent" class="com.tuling.testparentsonbean.TulingParentCompent" abstract="true">
<property name="tulingCompent" ref="tulingCompent"></property>
</bean>
<bean id="tulingSonCompent" class="com.tuling.testparentsonbean.TulingSonCompent" parent="tulingParentCompent"></bean>
*/
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
//检查当前创建的bean定义是不是抽象的bean定义
checkMergedBeanDefinition(mbd, beanName, args);
/**
*
* @Bean
public DependsA dependsA() {
return new DependsA();
}
@Bean
@DependsOn(value = {"dependsA"})
public DependsB dependsB() {
return new DependsB();
}
* 处理dependsOn的依赖(这个不是我们所谓的循环依赖 而是bean创建前后的依赖)
*/
//依赖bean的名称
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
// <1> 若给定的依赖 bean 已经注册为依赖给定的 bean
// 即循环依赖的情况,抛出 BeanCreationException 异常
for (String dep : dependsOn) {
//beanName是当前正在创建的bean,dep是正在创建的bean的依赖的bean的名称
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
//保存的是依赖 beanName 之间的映射关系:依赖 beanName - > beanName 的集合
registerDependentBean(dep, beanName);
try {
//获取depentceOn的bean
getBean(dep);
} catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
//创建单例bean
if (mbd.isSingleton()) {
//把beanName 和一个singletonFactory 并且传入一个回调对象用于回调
sharedInstance = getSingleton(beanName, () -> {
try {
//进入创建bean的逻辑
return createBean(beanName, mbd, args);
} catch (BeansException ex) {
//创建bean的过程中发生异常,需要销毁关于当前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();
if (!StringUtils.hasLength(scopeName)) {
throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
}
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, () -> {
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;
}
}
...
return (T) bean;
}
还是老套路,我们配合图加如上代码一起分析,会更加容易理解一点
看到 transformedBeanName();该方法主要作用是获取最真实的Bean的名称,为什么要这么说呢?
因为我们在将类注册成Bean定义的时候,是可以设置它的别名的,如果不设置Spring默认会以类名的驼峰命名来注册Bean
如果是设置了别名,通过反射是无法去实例化对象的,所以这里是需要获取到它最真实的类名称啦
然后我们往下分析
getSingleton()方法,该方法会去我们的单例池里面寻找,我们的Bean是否已经被创建了,如果被创建了则直接返回
但是我们现在还只是在初始化阶段,想都不用想都知道这里肯定是找不到的啦
当然Spring里的三级缓存也在方法中,我们进去瞧上一波哈
我们可以先看到源码中的三级缓存是啥样子
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/** 一级缓存 这个就是我们大名鼎鼎的单例缓存池 用于保存我们所有的单实例bean */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
/** 三级缓存 该map用户缓存 key为 beanName value 为ObjectFactory(包装为早期对象) */
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
/** 二级缓存 ,用户缓存我们的key为beanName value是我们的早期对象(对象属性还没有来得及进行赋值) */
private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);
}
从如上源码中,我们可以看看三个集合缓存的有什么区别?
我们可以看到他们的缓存集合类型都是Map,且key是String,value是Object类型,只有三级缓存不一样,三级缓存的它的value是ObjectFactory类型
大家伙是否很好奇,为啥三级缓存是value是这个类型呢?
我们现在暂时不深究,待会我们一起看源码就知道有什么作用了,这里我们只需要知道他们的区别即可
获取单例源码如下
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
/**
* 第一步:我们尝试去一级缓存(单例缓存池中去获取对象,一般情况从该map中获取的对象是直接可以使用的)
* IOC容器初始化加载单实例bean的时候第一次进来的时候 该map中一般返回空
*/
Object singletonObject = this.singletonObjects.get(beanName);
/**
* 若在第一级缓存中没有获取到对象,并且singletonsCurrentlyInCreation这个list包含该beanName
* IOC容器初始化加载单实例bean的时候第一次进来的时候 该list中一般返回空,但是循环依赖的时候可以满足该条件
*/
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
synchronized (this.singletonObjects) {
/**
* 尝试去二级缓存中获取对象(二级缓存中的对象是一个早期对象)
* 何为早期对象:就是bean刚刚调用了构造方法,还来不及给bean的属性进行赋值的对象(纯净态)
* 就是早期对象
*/
singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null) {
/**
* 二级缓存中也没有获取到对象,allowEarlyReference为true(参数是有上一个方法传递进来的true)
*/
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
/**
* 在这里通过暴露的ObjectFactory 包装对象中,通过调用他的getObject()来获取我们的早期对象
* 在这个环节中会调用到 getEarlyBeanReference()来进行后置处理
*/
singletonObject = singletonFactory.getObject();
//从三级缓存中获取到对象不为空
this.earlySingletonObjects.put(beanName, singletonObject);
//ObjectFactory 包装对象从三级缓存中删除掉
this.singletonFactories.remove(beanName);
}
}
}
}
}
}
return singletonObject;
}
1.首先从一级缓存通过bean的原始名称到singletonObjects集合中去获取,如果获取到直接return
2.如果从一级缓存中获取不到结果或者对象正在创建中(isSingletonCurrentlyInCreation(beanName)),那就从二级缓存通过bean的原始名称到earlySingletonObjects集合中去获取,如果获取到直接return
3.如果一级缓存还是获取不到结果,且allowEarlyReference为true的话,这个时候就会从三级缓存(singletonFactories).getObject()去获取
4.如果三级缓存获取到结果了,就将获取到的结果put到二级缓存(earlySingletonObjects)中,并且从三级缓存(singletonFactories)集合中remove掉
看完获取单例源码后,我们接着回到doGetBean()方法,接着往下分析
我们这里直接看到重点地方,其他的方法我都有写上注释,各位小伙伴知道那些方法的作用即可啦
这里会再一次判断当前Bean是否为单例,我这里是单例Bean,则进入判断
小伙伴们注意了,这里的getSingleton()方法,并不是我上面分析从单例池获取Bean的哦
这里会根据传入的beanName,然后去创建Bean操作了哦,这里使用到了函数编程啦,这里不作多介绍了,不太了解函数编程的小伙伴,可以自行百度一下哦
好,那么我们就直接进入到createBean方法啦,去看看Spring是如何创建Bean的哦
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
if (logger.isTraceEnabled()) {
logger.trace("Creating instance of bean '" + beanName + "'");
}
RootBeanDefinition mbdToUse = mbd;
// 确保此时的 bean 已经被解析了
Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// Prepare method overrides.
try {
/**
* 验证和准备覆盖方法( 仅在XML方式中)
* lookup-method 和 replace-method
* 这两个配置存放在 BeanDefinition 中的 methodOverrides( 仅在XML方式中)
* 在XML方式中 bean 实例化的过程中如果检测到存在 methodOverrides ,
* 则会动态地位为当前 bean 生成代理并使用对应的拦截器为 bean 做增强处理。
* 具体的实现我们后续分析,现在先看 mbdToUse.prepareMethodOverrides() 代码块
*/
mbdToUse.prepareMethodOverrides();
} catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
beanName, "Validation of method overrides failed", ex);
}
try {
/**
* 第一个bean后置处理器
* 通过bean的后置处理器来进行后置处理生成代理对象,一般情况下在此处不会生成代理对象
* 为什么不能生成代理对象,不管是我们的jdk代理还是cglib代理都不会在此处进行代理,因为我们的
* 真实的对象没有生成,所以在这里不会生成代理对象,那么在这一步是我们aop和事务的关键,因为在这里
* 解析我们的aop切面信息进行缓存
*/
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
} catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
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) {
// A previously detected exception with proper bean creation context already,
// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
throw ex;
} catch (Throwable ex) {
throw new BeanCreationException(
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
}
}
老样子,我们直接看到重点部分,其他部分小伙伴们看注释理解即可
我们进入到doCreateBean方法
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
//BeanWrapper 是对 Bean 的包装,其接口中所定义的功能很简单包括设置获取被包装的对象,获取被包装 bean 的属性描述器
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
//从没有完成的FactoryBean中移除
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
/**
* 实例化对象,第二次调用后置处理器
* 创建bean实例化 使用合适的实例化策略来创建新的实例:工厂方法、构造函数自动注入、简单初始化 该方法很复杂也很重要
*/
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
//从beanWrapper中获取我们的早期对象(从getWrappedInstance就能看出获取构造方法,也就是获取它的实例,相当于new出来的对象)
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
/**
* 第三次调用后置处理器
* 进行后置处理 @AutoWired @Value的注解的预解析
*/
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,如果是,则允许提前暴露引用
* 判断是否能够暴露早期对象的条件:
* 是否单例
* 是否允许循环依赖(重点,Spring默认为true)
* 是否正在创建的Bean
*/
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");
}
/**
* 第四次调用后置处理器,判断是否需要AOP
* 把我们的早期对象包装成一个singletonFactory对象 该对象提供了一个getObject方法,该方法内部调用getEarlyBeanReference方法
*/
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
//属性赋值 给我们的属性进行赋值(调用set方法进行赋值) --》 这里就是自动注入
//方法里面实现了第五次和第六次调用后置处理器
populateBean(beanName, mbd, instanceWrapper);
//进行对象初始化操作(在这里可能生成代理对象)
//方法里面实现了第七次和第八次调用后置处理器
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) {
/**
* 去缓存中获取到我们的对象 由于传递的allowEarlyReference 是false 要求只能在一级二级缓存中去获取
* 正常普通的bean(不存在循环依赖的bean) 创建的过程中,压根不会把三级缓存提升到二级缓存中
*/
Object earlySingletonReference = getSingleton(beanName, false);
//能够获取到
if (earlySingletonReference != null) {
//经过后置处理的bean和早期的bean引用还相等的话(表示当前的bean没有被代理过)
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
//处理依赖的bean
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(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.");
}
}
}
}
// Register bean as disposable.
try {
//注册销毁的bean的销毁接口
registerDisposableBeanIfNecessary(beanName, bean, mbd);
} catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}
return exposedObject;
}
配合图,我们直接看到重点部分,createBeanInstance()该方法的主要作用在于,其底层方法会推断出一个合适的构造器,通过反射创建实例化我们的对象,该底层方法我就不跟进去了
我们通过debug调试,看到instanceWrapper是我们注入的类的类型,然后看到wrappedObject里面的属性还是为null,所以我们暂时可以证明Spring还没有给我们的属性进行属性注入
也可以看到我刚执行完instanceWrapper = createBeanInstance(beanName, mbd, args); 我的控制台立马输出无参构造方法的信息,这也就证明了,该方法就是去new出我们的对象的啦
我们继续往下走,看到下图标记的这一行代码,这个地方就是判断Spring是否开启循环依赖的地方,当然啦,我们看到最主要的一个地方,就是this.allowCircularReferences这个属性,该属性的值默认是为true的,所以Spring是默认开启循环依赖的
且主要看到addSingletonFactory()方法,它的底层会提前将当前的Bean放入的三级缓存中,现阶段可以理解将Bean提前暴露出来了
注意哦,这个三级缓存 存储的value是个工厂哦,为什么是存储工厂呢?
首先if判断是需要支持循环依赖,才会进入到addSingletonFactory()方法的,换个思路吧,我上面有说到,我们虽说创建了对象,但是到当前这一步,还没有进行属性赋值,如果没有当前这一步(不将值插入到三级缓存中),走到下面要进行属性赋值的时候,会一直循环的赋值(因为我们的属性是需要注入的,当发现需要注入,又会重新回到前面的getBean()方法那里,重新走一遍),如果我们不放入到缓存中,他会一直创建,而有了缓存直接从缓存读取,节省了很多时间,且我们要知道我们存储的工厂还是能够继续对Bean进行修改的,因为我们的对象还没有成为Bean
protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(singletonFactory, "Singleton factory must not be null");
synchronized (this.singletonObjects) {
if (!this.singletonObjects.containsKey(beanName)) {
this.singletonFactories.put(beanName, singletonFactory);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
关闭Spring循环依赖
当然既然Spring默认支持循环依赖,那我想关闭怎么办呢?在这里有以下几种方法关闭
1.通过修改源码的方式,达到关闭Spring循环依赖的效果
2.通过AbstractAutowireCapableBeanFactory里面的API的形式,来继续关闭Spring循环依赖
3.通过Spring的扩展点,来达到关闭Spring循环依赖的效果
好了,我们在接着回到上面开启循环依赖的代码处,继续往下分析,看到populateBean(beanName, mbd, instanceWrapper);这行代码
这行代码就是我们常说的依赖注入啦,给我们的属性注入值,但是可以看到我的debug调试的控制台可以看到
我的属性注入已经开始发生了循环注入
好,我们知道了populateBean(beanName, mbd, instanceWrapper)该方法的是属性注入,我们跟进去
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
//若bw为null的话,则说明对象没有实例化
if (bw == null) {
//进入if 说明对象有属性,bw为空,不能为他设置属性,那就在下面就执行抛出异常
if (mbd.hasPropertyValues()) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
}
else {
// Skip property population phase for null instance.
return;
}
}
/**
* 在属性被填充前,给 InstantiationAwareBeanPostProcessor 类型的后置处理器一个修改
* bean 状态的机会。官方的解释是:让用户可以自定义属性注入。比如用户实现一
* 个 InstantiationAwareBeanPostProcessor 类型的后置处理器,并通过
* postProcessAfterInstantiation 方法向 bean 的成员变量注入自定义的信息。
*当时我们发现系统中的的InstantiationAwareBeanPostProcessor.postProcessAfterInstantiationM没有进行任何处理,
*若我们自己实现了这个接口 可以自定义处理.....spring 留给我们自己扩展接口的
*特殊需求,直接使用配置中的信息注入即可。
*/
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
//是否持有 InstantiationAwareBeanPostProcessor
for (BeanPostProcessor bp : getBeanPostProcessors()) {
//判断我们的后置处理器是不是InstantiationAwareBeanPostProcessor
if (bp instanceof InstantiationAwareBeanPostProcessor) {
//进行强制转化
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
//若存在后置处理器给我们属性赋值了,那么返回false 可以来修改我们的开关变量,就不会走下面的逻辑了
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
return;
}
}
}
}
//获取bean定义的属性
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
/**
* 判断我们的bean的属性注入模型
* AUTOWIRE_BY_NAME 根据名称注入
* AUTOWIRE_BY_TYPE 根据类型注入
*/
int resolvedAutowireMode = mbd.getResolvedAutowireMode();
if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
//把PropertyValues封装成为MutablePropertyValues
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
//根据bean的属性名称注入
if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
//根据bean的类型进行注入
if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
//把处理过的 属性覆盖原来的
pvs = newPvs;
}
/**
* 这里又是一种后置处理,用于在 Spring 填充属性到 bean 对象前,对属性的值进行相应的处理,
* 比如可以修改某些属性的值。这时注入到 bean 中的值就不是配置文件中的内容了,
* 而是经过后置处理器修改后的内容
*/
boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
//判断是否需要检查依赖
boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
//提出当前正在创建的beanWrapper 依赖的对象
PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
//获取所有的后置处理器
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
//对依赖对象进行后置处理
pvs = pvsToUse;
}
}
}
//判断是否检查依赖
if (needsDepCheck) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
checkDependencies(beanName, mbd, filteredPds, pvs);
}
/**
* 其实,上面只是完成了所有注入属性的获取,将获取的属性封装在 PropertyValues 的实例对象 pvs 中,
* 并没有应用到已经实例化的 bean 中。而 #applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) 方法,
* 则是完成这一步骤的
*/
if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
我们知道,前面创建好的对象,并不是一个完整的对象,里面的属性还没有被赋值,所以该方法的作用就是为创建好的对象给它的属性赋值,并且调用实现了InstantiationAwareBeanPostProcessors的后置处理器,可以给我们最后一次修改Bean的属性的机会,其他的不多说了,看到我标记的这两行代码,就是属性注入的核心代码,在populateBean里面他会解析你的属性,并且赋值,当发现InstanceA对象里面依赖instanceB,此时又会走getBean()方法,但这个时候你去缓存中去拿是可以拿到的,因为我们在对 createBeanInstance 对象创建完成以后已经放入了缓存当中,所以创建 InstanceB 的时候发现依赖 instanceA,直接就从缓存中去拿,那么此时instanceA和instanceB都创建好了,到此Bean也创建完成,最后将创建好的Bean放入到一级缓存中,也就是我们常说的单例池中
我们可以看到上面有一个for循环,会遍历所有的后置处理器,当然哦,我这里会遍历三次,每一次遍历都是不同的后置处理器,可以结合我下图查看
其中第二张图和第三张图,它们的后置处理器分别为CommonAnnotationBeanPostProcessor和AutowiredAnnotationBeanPostProcessor
CommonAnnotationBeanPostProcessor的后置处理器主要处理@Resource注解的属性注入
AutowiredAnnotationBeanPostProcessor的后置处理器主要处理@Autowired注解的属性注入
所以我们看到三张图,因为我们的属性注入使用的@Autowired注解,所以会将其他的属性全部实例化掉,也就在控制台输出了信息
扩展延申
1.一级缓存解决循环依赖的问题(一级缓存读取肯定完整的Bean)
详细描述:加入将类A和类B都注入到Bean对象,然后在各自的类中,都有对方的对象,这样去注入的Bean,就会产生循环依赖的问题
解决方式:当去创建Bean A的时候,将BeanA加入到一级缓存,再去创建Bean B的时候,去检查一级缓存是否有该实例,如果有该实例,则不再去创建,是否就已经解决的循环依赖的问题呢
2.二级缓存解决防止多线程下会读取到不成熟的Bean(分隔成熟Bean和不成熟的Bean)
详细描述:就上面的问题描述延申,如果在多线程情况下,我一个线程刚创建Bean对象A,但是还没有实例化属性B,这个时候Bean已经加入到一级缓存中去了,我另外一个线程恰好,也去创建Bean A,发现已经创建好,直接去读取,那么这个时候去读取的Bean里的属性肯定为NULL,那这个时候读取到的Bean就是一个不完整的Bean
解决方式:通过添加二级缓存,去解决不成熟Bean(多线程下存储的可能是不完整的Bean),然后一级缓存是最后存储的Bean,也就是说,一级缓存存储的是完整的Bean
3.三级缓存是解决
详细描述:防止Bean对象重复创建,我们知道三级缓存的value存放的是一个工厂,假如我两个类都注入了相同的属性,那我去实例化一个工厂的时候大概需要10s,有了三级缓存则在缓存中读取,无需重复创建,也节省创建的时间
解决方案:
严谨的说法:
1.在实例化Bean的时候去创建AOP(Spring希望正常的Bean,也就是没有循环依赖的Bean 将AOP放在初始化之后去创建)
2.在实例化(初始化)Bean之后去创建AOP(Spring希望循环依赖的Bean 将AOP放在实例化之后去创建)
3.其实AOP也可以在二级缓存就解决了,为什么要三级缓存中实现呢, Spring每个方法只干一件事情,可能因为单一原则或者是解耦的原因吧,所以才放到三级缓存中
至此,Spring循环依赖源码解析这篇文章就到此结束啦。