十一、循环依赖
1、依赖注入方式
(1)、构造方法注入
如果循环依赖通过构造方法注入会抛出BeanCurrentlyInCreationException异常。例如:A类通过构造函数注入需要B类的实例,而B类通过构造函数注入需要A类的实例,如果将A类和B类的bean配置为互相注入,则SpringIOC容器会在运行时检测到此循环依赖,并抛出BeanCurrentlyInCreationException异常。以下这种情况,编译不会通过。
如果类A、类B的构造函数无参,只有new A()和new B()操作,启动会抛出java.lang.StackOverflowError错误。
public class A {
private B b;
/**
* 通过构造方法注入
*/
public A (B b){
this.b = b;
}
}
public class B {
private A a;
/**
* 通过构造方法注入
*/
public B(A a){
this.a = a;
}
}
(2)、Setter方法注入
通过Setter方式注入能够解决循环依赖问题,但是注入方式需要是singleton,其他方式依然会报错。
public class A {
private B b;
/**
* 通过set方法注入B
*/
public void setB (B b){
this.b = b;
}
}
public class B {
private A a;
/**
* 通过set方法注入A
*/
public void setA(A a){
this.a = a;
}
}
(3)、接口注入
2、scope(作用域)
Scope字面意思是范围,Spring中用来声明IOC器中的对象应该处的限定场景或者说该对象的存活空间,即在IOC器在对象进入相应的scope之前,生成并装配这些对象,在该对象不再处于这些scope的限定之后,容器通常会销毁这些对象。
(1)、设置方式
①、注解
@Configuration
public class ProtoTypeConfig {
@Scope("singleton")
@Bean
public A a() {
return new A();
}
}
②、xml配置
<bean id="a" class="com.thc.bpm.A" scope="singleton" />
(2)、模式
①、singleton(不写默认)
a、单例模式,全局有且仅有一个实例,也就是说在整个Spring IOC容器中,使用singleton定义的Bean将有且仅有只有一个实例。
②、prototype
原型模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例。
prototype模式下出现循环依赖会抛出BeanCurrentlyInCreationException异常。
③、request
对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。同时该Bean实例仅在当前HTTP Request请求内有效。
④、session
对于每次HTTP Session,使用session定义的Bean都将产生一个新实例。同时该bean仅在当前HTTP session内有效。
⑤、globalsession
每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。
Session知识补充
Session是用于存放用户与web服务器之间的会话,即服务器为客户端开辟的存储空间。Session存在的意义是为了提高安全性,它将关键数据存在服务器端,与cookie不同,cookie则是将数据存在客户端的浏览器中。Sessinon在用户访问第一次访问服务器时创建,需要注意只有访问JSP、Servlet等程序时才会创建Session,只访问HTML、IMAGE等静态资源并不会创建Session,可调用request.getSession(true)强制生成Session。Tomcat中Session的默认失效时间为20分钟。
3、DefaultSingletonBeanRegistry类
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/**
* 第一级缓存/单例缓存池
* Map<Bean名称, Bean实例>
* 存放已经完全初始化完成的Bean对象,从该缓存中取出的Bean可以直接使用
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
/**
* 第二级缓存
* Map<Bean名称, Bean实例>
* 存放原始的Bean对象,只是进行了实例化,但没有初始化(尚未填充属性),用于解决循环依赖
*/
private final Map<String, Object> earlySingletonObjects = new HashMap(16);
/**
* 第三级缓存
* Map<Bean名称, ObjectFactory>
* 存放可以生成Bean的Bean工厂对象(lamda表达式),用于解决循环依赖
*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
}
4、解决循环依赖
(1)、前提条件
①、通过Setter注入
Spring解决循环依赖依靠的是Bean的“中间态”这个概念,而这个中间态指的是已经实例化,但还没初始化的状态(内部属性未填充)。而构造器是完成实例化的,没有办法处理中间态,所以构造器的循环依赖无法解决。
②、Bean为单例
只有单例的Bean才能通过三级缓存提前暴露解决循环依赖的问题,而非单例的Bean,每次从容器中获取都是一个新的实例,都会重新创建,所以非单例的Bean是没有缓存的,不会将其放在三级缓存中。
(2)、流程
假设类A、类B循环引用。
①、执行getSingleton()方法从一级缓存获取A实例,获取到返回,如果没有获取到,执行doCrateBean()方法实例化A,并将A放入三级缓存中。然后执行populateBean()方法对A进行初始化,此时发现依赖B。
②、执行getSingleton()方法从一级缓存获取B实例,获取到返回,如果没有获取到执行doCrateBean()方法实例化B,并将B放入三级缓存中。然后执行populateBean()方法对B进行初始化,此时发现B又依赖A。
③、从缓存一级缓存中查找,没有,再查二级缓存,再查三级缓存找到早期暴露的A,然后将A放到二级缓存里,并删除三级缓存里的A。如果有AOP代理先获取代理后的对象A,没有的话,就直接将A的原始对象注入B,继续执行对象B的属性填充和初始化。
④、B的属性填充和初始化完成后,将初始化完的B实例放到一级缓存里面(此时B里面的A依然是中间态)。然后继续执行A的属性填充操作,如果没有AOP代理,就直接从一级缓存拿到B,将B的实例注入A中,并将A放到一级缓存。
5、循环依赖源码解析
(1)、DefaultSingletonBeanRegistry源码
该类定义三级缓存。
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/**
* 第一级缓存/单例缓存池
* 存放已经完全初始化完成的Bean对象,从该缓存中取出的Bean可以直接使用
*/
private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
/**
* 第二级缓存
* 存放原始的Bean对象,只是进行了实例化,但没有初始化(尚未填充属性),用于解决循环依赖
*/
private final Map<String, Object> earlySingletonObjects = new HashMap(16);
/**
* 第三级缓存
* 存放可以生成Bean的Bean工厂对象(lamda表达式),用于解决循环依赖
*/
private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
/**
* 在Bean开始创建时放入,创建完成时会将其移出
*/
private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap(16));
/**
* 从一级缓存获取Bean
*/
@Nullable
public Object getSingleton(String beanName) {
// 传入beanName,allowEarlyReference设置为true表示允许早期引用
return this.getSingleton(beanName, true);
}
/**
* 从一级缓存中获取Bean
*/
@Nullable
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// Map<String, Object> singletonObjects,从第一级缓存中获取Bean
Object singletonObject = this.singletonObjects.get(beanName);
// isSingletonCurrentlyInCreation方法底层是判断singletonsCurrentlyInCreation的Set集合(存放正在创建的bean的集合)中是否包含beanName
// 从第一级缓存获取结果为空 && bean没有正在创建
if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {
Map var4 = this.singletonObjects;
// 同步代码块
synchronized (this.singletonObjects) {
// 从第二级缓存中查找
singletonObject = this.earlySingletonObjects.get(beanName);
// 从第二级缓存获取结果为空 && 允许早期引用
if (singletonObject == null && allowEarlyReference) {
// 从第三级缓存查找
ObjectFactory<?> singletonFactory = (ObjectFactory) this.singletonFactories.get(beanName);
// 从第二级缓存获取结果不为空
if (singletonFactory != null) {
// 获取第三级缓存中beanName对应的Object
singletonObject = singletonFactory.getObject();
// 放入第二级缓存
this.earlySingletonObjects.put(beanName, singletonObject);
// 从第三级缓存中移除
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
/**
* 重载方法
* @param singletonFactory
* singletonFactory可能为的实例化bean的lamda表达式
*/
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {......}
}
(2)、AbstractBeanFactory源码
public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
/**
* 根据Bean名称,获取Bean实例
*/
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
// beanName转换,获取去掉前缀的beanName,因为缓存里存的beanName没有以&为前缀的修饰符等
String beanName = this.transformedBeanName(name);
// 从缓存中获取bean
Object sharedInstance = this.getSingleton(beanName);
Object bean;
if (sharedInstance != null && args == null) {
......
} else {
......
// 如果不是只检查类型,那就标记这个bean被创建
if (!typeCheckOnly) {
this.markBeanAsCreated(beanName);
}
try {
......
//创建bean实例
if (mbd.isSingleton()) {
// 这个getSingleton方法不是SingletonBeanRegistry的接口方法,是其实现类DefaultSingletonBeanRegistry的一个public重载方法
// getSingleton(String beanName, ObjectFactory<?> singletonFactory)重载方法的特点
// 是在执行singletonFactory.getObject();前后会执行beforeSingletonCreation(beanName);和afterSingletonCreation(beanName);
// 也就是保证这个Bean在创建过程中,放入正在创建的缓存池里,可以看到它实际创建bean调用的是我们的createBean方法
sharedInstance = this.getSingleton(beanName, () -> {
try {
return this.createBean(beanName, mbd, args);
} catch (BeansException var5) {
this.destroySingleton(beanName);
throw var5;
}
});
bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} else if (mbd.isPrototype()) {......} else {......}
} catch (BeansException var26) {
......
}
}
......
}
}
(3)、AbstractAutowireCapableBeanFactory源码
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
/**
* 创建Bean实例
*/
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {
// 实例化后的对象会被封装在BeanWrapper对象
BeanWrapper instanceWrapper = null;
......
if (instanceWrapper == null) {
// 使用构造器/工厂方法,创建bean对象,并且将对象包裹在BeanWrapper中
instanceWrapper = this.createBeanInstance(beanName, mbd, args);
}
// 从BeanWrapper中获取bean的原始对象
Object bean = instanceWrapper.getWrappedInstance();
// 从BeanWrapper中获取bean的类信息
Class<?> beanType = instanceWrapper.getWrappedClass();
......
// earlySingletonExposure用于表示是否”提前暴露“原始对象的引用,用于解决循环依赖。
// mbd.isSingleton()根据该scope设置的属性,判断是不是设置的单例模式
// allowCircularReferences表示是否允许循环引用,对于单例Bean,该变量一般为true,但你也可以通过属性allowCircularReferences = false来关闭循环引用。
// isSingletonCurrentlyInCreation(beanName) 表示当前bean必须在创建中。判断该beanName是否包含在DefaultSingletonBeanRegistry对象的singletonsCurrentlyInCreation变量中。
boolean earlySingletonExposure = mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);
if (earlySingletonExposure) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references");
}
// 传入ObjectFactory对象也就是lamda表达式,并将该beanName和objectFactory放入第三级缓存,并从第二级缓存移除
this.addSingletonFactory(beanName, () -> {
// getEarlyBeanReference的作用:调用SmartInstantiationAwareBeanPostProcessor.getEarlyBeanReference()这个方法
// 也就是给调用者个机会,自己去实现暴露这个bean的应用的逻辑
// 比如在getEarlyBeanReference()里可以实现AOP的逻辑,参考自动代理创建器AbstractAutoProxyCreator实现了这个方法来创建代理对象
// 若不需要执行AOP的逻辑,直接返回bean
return this.getEarlyBeanReference(beanName, mbd, bean);
});
}
Object exposedObject = bean;
try {
// 给bean注入相关的依赖
this.populateBean(beanName, mbd, instanceWrapper);
// 执行初始化回调方法
// invokeAwareMethods - 激活Aware方法
// (1)、如果bean实现了BeanNameAware 接口,则将 beanName设值进去
// (2)、如果bean实现了BeanClassLoaderAware接口,则将 ClassLoader 设值进去
// (3)、如果bean实现了BeanFactoryAware接口,则将 beanFactory 设值进去
// invokeInitMethods - 激活自定义的init方法
// (1)、Bean的初始化方法除了可以使用 init-method 属性(或者 @Bean(initMethod=''”)),还可以通过实现InitializingBean接口,并且在afterPropertiesSet 方法中实现自己初始化的业务逻辑。
exposedObject = this.initializeBean(beanName, exposedObject, mbd);
} catch (Throwable var18) {
......
}
// earlySingletonExposure:如果你的bean允许被早期暴露出去 也就是说可以被循环引用 那这里就会进行检查
if (earlySingletonExposure) {
// 从一级缓存获取数据,第二参数为false表示不会再去三级缓存里查。
// 但是此时一级、二级缓存都没数据
// 此处非常巧妙的一点:::因为上面各式各样的实例化、初始化的后置处理器都执行了,如果你在上面执行了这一句
// ((ConfigurableListableBeanFactory)this.beanFactory).registerSingleton(beanName, bean);
// 那么此处得到的earlySingletonReference的引用最终会是你手动放进去的Bean最终返回,完美的实现了"偷天换日" 特别适合中间件的设计
// 我们知道,执行完此doCreateBean后执行addSingleton() 其实就是把自己再添加一次 **再一次强调,完美实现偷天换日**
Object earlySingletonReference = this.getSingleton(beanName, false);
if (earlySingletonReference != null) {
// 经过了initializeBean()后,exposedObject还是没有变,那就直接返回。
// initializeBean会调用后置处理器,这个时候可以生成一个代理对象,就不会相等,进入else
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
// allowRawInjectionDespiteWrapping这个值默认是false
// hasDependentBean:若它有依赖的bean 那就需要继续校验,没有继续执行
} else if (!this.allowRawInjectionDespiteWrapping && this.hasDependentBean(beanName)) {
// 获取它所依赖的一系列Bean
String[] dependentBeans = this.getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet(dependentBeans.length);
String[] var12 = dependentBeans;
int var13 = dependentBeans.length;
// 循环遍历所依赖的Bean
for(int var14 = 0; var14 < var13; ++var14) {
String dependentBean = var12[var14];
// removeSingletonIfCreatedForTypeCheckOnly这个方法在AbstractBeanFactory里面
// 就是判断到该dependentBean是不是没有创建完成,如果没有创建完成,返回true。
// 否则创建完成就返回false,进入我们的if里面,表示所谓的真正依赖。
//(解释:就是真的需要依赖它先实例化,才能实例化自己的依赖)
// 就是如果判断到该dependentBean并没有在创建中,那就把它从所有缓存中移除,并且返回true,后续报错提示有依赖的bean没有创建。
// 否则(比如确实在创建中) 那就返回false,进入我们的if里面,表示所谓的真正依赖。
//(解释:就是真的需要依赖它先实例化,才能实例化自己的依赖)
if (!this.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.");
}
}
}
}
try {
this.registerDisposableBeanIfNecessary(beanName, bean, mbd);
return exposedObject;
} catch (BeanDefinitionValidationException var16) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", var16);
}
}
}