1. Spring IOC容器初始化过程:
2. bean的生命周期
- Bean 容器找到配置文件中 Spring Bean 的定义。(beanDefintion)
- Bean 容器利用 Java Reflection API 创建一个Bean的实例。(执行构造方法)
- 如果涉及到一些属性值 利用 set()方法设置一些属性值。(set属性)
- 如果 Bean 实现了 BeanNameAware 接口,调用 setBeanName()方法,传入Bean的名字。(aware接口的相关方法)
- 如果 Bean 实现了 BeanClassLoaderAware 接口,调用 setBeanClassLoader()方法,传入
ClassLoader对象的实例。 - 与上面的类似,如果实现了其他 *.Aware接口,就调用相应的方法。
- 如果有和加载这个 Bean 的 Spring 容器相关的 BeanPostProcessor
对象,执行postProcessBeforeInitialization() 方法 (BeanPostProcessor的before方法) - 如果Bean实现了InitializingBean接口,执行afterPropertiesSet()方法。(afterPropertiesSet方法)
- 如果 Bean 在配置文件中的定义包含 init-method 属性,执行指定的方法。(init-method方法)
- 如果有和加载这个 Bean的 Spring 容器相关的 BeanPostProcessor
对象,执行postProcessAfterInitialization() 方法 (BeanPostProcessor的after方法(生成代理类)) - 当要销毁 Bean 的时候,如果 Bean 实现了 DisposableBean 接口,执行 destroy() 方法。(destroy方法)
- 当要销毁 Bean 的时候,如果 Bean 在配置文件中的定义包含 destroy-method 属性,执行指定的方法。(destroy-method方法)
3. 什么是Spring的循环依赖?
一种是A依赖了B,B又依赖了A,
另一种情况是A自己依赖了自己。
无线套娃
4. Spring是如何解决循环依赖的?
Spring运用了三级缓存解决了循环依赖
- singletonObjects:用于存放完全初始化好的 bean,从该缓存中取出的 bean 可以直接使用
- earlySingletonObjects:提前曝光的单例对象的cache,存放原始的 bean 对象(尚未填充属性),用于解决循环依赖
- singletonFactories:单例对象工厂的cache,存放 bean 工厂对象,用于解决循环依赖
具体流程图如下
B中从三级缓存取出的A早期引用和A本身是一个引用,这样当A完成了初始化后,B中的A也完成了初始化
4.1 普通bean(未经代理的bean)解决循环依赖
如果单纯的解决普通bean循环依赖,A在赋值前,把早期bean放入缓存,然后B在赋值A时,从缓存中取出。其实用一层缓存就够了,为什么需要做3层缓存呢?
4.2 事务代理类解决循环依赖
上面说为什么需要做3层缓存,其中一个原因就是为了解决代理类的循环依赖。如果我们只用一层缓存,那么B依赖的就是一个普通的bean,而不是一个代理bean。
而我们的第三级缓存中调用的工厂方法
getEarlyBeanReference生成早期bean,并且会调用事务代理的后置处理器【AnnotationAwareAspectJAutoProxyCreator】的父类【AbstractAutoProxyCreator】的getEarlyBeanReference生成代理(如果有加事务注解)
protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) {
Object exposedObject = bean;
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) {
// 会调用事务的后置处理器AbstractAutoProxyCreator的getEarlyBeanReference方法生成代理
exposedObject = bp.getEarlyBeanReference(exposedObject, beanName);
}
}
return exposedObject;
}
【AbstractAutoProxyCreator】生成代理的方法做了处理,只会被代理一次,这样就解决了一个bean被多次代理(生成多个代理类)的问题
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 已经被代理则直接返回
if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
return bean;
}
// 代理的逻辑....
// 标记已经被代理
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
如果A被循环依赖且被事务代理,根据上面的逻辑,B中的A是代理类,而A自己因为循环依赖中的B先一步生成了A的代理,所以A不会在initializeBean中生成代理类。这样会导致两个bean不相等,所以spring在bean初始化的最后,会进行校验,如果A没有被代理并且缓存中有A,这样会把缓存中的A赋值A自己,这样就解决了两个A不一致的问题。
if (earlySingletonExposure) {
// 只从一二级缓存中找(是不是被循环依赖了)
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
// 创建的bean是否经BeanPostProcessor变成代理
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
// ...
}
}
4.3 没有循环依赖的bean怎么生成代理类
没有循环依赖的话,initializeBean方法中会调用beanPostProcessor的after方法,生成所有的代理(包括事务)
4.4 @async代理循环依赖报错
比如A加了@async,A->b->A就会循环依赖报错,
原因是bean在初始化最后校验中,如果发现A已经被代理,且被循环依赖,就会直接报错
(而加事务不会,因为A中还是普通的bean,代理bean的是B中的A,所以不会进这个逻辑)
(@async的后处理器【AsyncAnnotationBeanPostProcessor】跟【AnnotationAwareAspectJAutoProxyCreator】不同的是,【AnnotationAwareAspectJAutoProxyCreator】有一个getEarlyBeanReference方法,循环依赖的时getEarlyBeanReference会调用这个方法先一步生成代理类,而@async的代理只能在initializeBean方法中调用的beanPostProcessor的after方法来生成)
if (earlySingletonExposure) {
// 只从一二级缓存中找(是不是被循环依赖了)
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
// 创建的bean是否经BeanPostProcessor变成代理
// 还是普通bean
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
// 已经变成代理则报错
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 " +
"'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
参考文章
Spring getBean是如何解决循环依赖和多次动态代理
一文告诉你Spring是如何利用"三级缓存"巧妙解决Bean的循环依赖问题的
Spring的BeanFactoryPostProcessor和BeanPostProcessor
女同事问敖丙什么是 Spring 循环依赖?我…
Spring 中的 bean 生命周期?