Spring循环依赖
如图,类之间的相互引用就是循环依赖,spring是允许这样的循环依赖(前提是单例的,非构造方法注入的情况下)
原型模式时每次注入bean(例如B)都会创建一个新的bean实例,创建B的实例又会注入bean(C),再创建C的实例则又要创建A的实例。所以原型模式使用循环依赖会直接抛出BeanCurrentlyInCreationException
Spring解决循环依赖
首先,Spring内部维护了三个Map,也就是我们通常说的三级缓存。
-
singletonObjects 俗称“单例池”“容器”,缓存创建完成单例Bean的地方。
-
earlySingletonObjects 映射Bean的早期引用,也就是说在这个Map里的Bean不是完整的,甚至还不能称之为“Bean”,只是一个Instance.
-
singletonFactories 映射创建Bean的原始工厂
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201126000319961.gif#pic_center)
图片来源:https://blog.csdn.net/qq_35190492
所谓的循环依赖其实无非就是属性注入,或者自动注入, 故而搞明白循环依赖就需要去研究spring自动注入的源码;spring的属性注入属于spring bean的生命周期一部分。
理解Spring生命周期:
-
spring bean——受spring容器管理的对象,可能经过了完整的spring bean生命周期,最终存在spring容器当中;一个bean一定是个对象
-
对象——任何符合java语法规则实例化出来的对象,但是一个对象并不一定是spring bean;
Spring生命周期
Class--------》beanDefinition----------》bean 与普通对象不同的是bean对象要经过
首先,Java虚拟机将类的class文件加载到方法区,然后扫描是否有注入bean的操作。 对于要注入的,spring会new一个BeanDefination
接口(这个接口包括各种对bean的属性判断方法,例如GenericBeanDefinition
)的实现类,然后将实现类对象即bean的各种属性(属性例如:beanClassName=“A”, beanClass=A.class,scope=“singleton”,…)put到BeanDefinitionMap
的beanDefinitionMap中去,通过finishiBeanFactoryInitialization(beanFactory)方法获取beanDefinitionMap中的beanName开始验证,验证成功后new一个bean对象放到spring单例池SingletonObjects
(map类型)中,【注意:原型对象不是在spring初始化的时候new一个bean,而是在getBean的时候】bean对象到单例池之前还可以调用BeanFactoryPostProcessor
接口的实现类进行扩展。
具体步骤如下:
1.扫描:扫描是否有bean注入
例如通过xml方式,JavaConfig方式或注解方式的bean注入。
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
this();
register(annotatedClasses);//注册配置类
refresh();// 真正初始化bean的方法
}
2.解析:解析是不是单例模式,是否懒加载…
在一个for循环中 {
new一个BeanDefinition
类的子类GenericBeanDefinition
对象genericBeanDefinition,这个对象包括setBeanClass,setBeanClassName,setScope…等方法。
定义一个BeanDefinitionMap
的beanDefinitionMap,将GenericBeanDefinition
对象put到beanDefinitionMap<“xxx”,genericBeanDefinition>中。
此外还会定义一个List的beanNames,beanNames.add(xxx),负责存储beanDefinitionMap中的key,用于遍历GenericBeanDefinition
对象。
}
3.调用扩展:查看是否赋予额外的扩展功能
如何扩展:
创建一个类实现BeanFactoryPostProcessor
接口,继承postProcessBeanFactory方法,方法会传一个beanFactory(就是Spring工厂,Spring工厂中就包括了上一步定义的beanDefinitionMap),通过beanFactory调用getBeanDefinition获取beanDefinitionMap中的BeanDefinition对象。
// 扩展实现,更改beanClass
@Component //加上才会扫描到,否则不起作用
public class test implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
GenericBeanDefinition bd = beanFactory.getBeanDefinition("A");
// 这里写一些扩展功能,比如更改beanName,禁止循环依赖...
bd.setBeanClass(B.Class);
}
}
// getBeanDefinition内部方法
@Override
public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
// 在这里获取map中的beanDefinition对象,beanName未声明则默认为自动驼峰命名。
BeanDefinition bd = this.beanDefinitionMap.get(beanName);
if (bd == null) {
if (this.logger.isTraceEnabled()) {
this.logger.trace("No bean named '" + beanName + "' found in " + this);
}
throw new NoSuchBeanDefinitionException(beanName);
}
return bd;
}
4.验证:根据第二步解析获得的属性判断要不要new一个Bean
具体判断bean是不是原型的;装配模式是byType还是byName;构造方法有没有参数,参数合不合理;
合理就实例化Bean:new一个 Bean对象到Spring的单例池SingletonObjects
(Map类型)中。
验证代码
finishBeanFactoryInitialization
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// 。。。
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}
preInstantiateSingletons
public void preInstantiateSingletons() throws BeansException {
//。。。
// 取出beanDefinitionMap存放的beanNames
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// 遍历并验证beanNames,进行实例化
for (String beanName : beanNames) {
// 验证是不是单例、懒加载。。。
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
//。。。
//实例化方法
getBean(beanName);
//。。。
}
}
}
说明
// 实例化所有还剩下的、非懒加载的单例对象
finishiBeanFactoryInitialization(beanFactory);
// 内部继续调用preInstantiateSingletons方法
beanFactort.preInstantiateSingletons();
// 获取第二步存入List的beanNames,进行遍历验证,例如是不是单例,是否懒加载...等,验证之后开始实例化
getBean(beanName);
// 内部调用doGetBean方法
5.实例化:实例化Bean
一级缓存: private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
二级缓存: private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
三级缓存: private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
getBean
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
doGetBean
protected <T> T doGetBean(final String name, 。。。) throws BeansException {
// 验证beanName合法性
final String beanName = transformedBeanName(name);
// 检查单例池中是否已经有了这个bean
Object sharedInstance = getSingleton(beanName);
// 若单例池中没有此bean,
if (sharedInstance != null && args == null) {...}
else {
// 判断这个类是不是在创建过程中,是则抛出异常
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
//继续进行一系列验证...
// 如果是单例
if (mbd.isSingleton()) {
//...
// bean开始创建
return createBean(beanName, mbd, args);
}
//...
}
getSingleton //判断单例池中是否已经有了这个bean
/** 单例对象缓存/单例池(beanName->bean实例): bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
// 如果单例池中没有这个bean,且当前bean正在创建,则去三级缓存中取bean。
// 循环依赖中,第一次初始化bean时‘正在创建’条件不成立,当循环到第二次初始化bean时,‘正在创建’条件成立
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
// 条件一旦成立,就会从三级缓存中取出bean,移到二级缓存中
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;
}
createBean
protected Object createBean(String beanName, RootBeanDefinition mbd, ...)throws BeanCreationException {
//...
RootBeanDefinition mbdToUse = mbd;
//...
// 第一次调用后置处理器,允许用代理对象替换目标对象
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
// ...
// 进入doCreateBean方法创建bean实例
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
}
doCreateBean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)throws BeanCreationException {
// 实例化bean
BeanWrapper instanceWrapper = null;
// 单例的,则从bean工厂里移除
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 实例化对象,createBeanInstance里第二次调用后置处理器(通过构造方法反射实例化对象)
instanceWrapper = createBeanInstance(beanName, mbd, args);
//。。。
// 第三次后置处理器,修改合并的bean定义
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
//。。。
// 是否允许循环依赖(单例的&&允许循环依赖&&正在创建)【allowCircularReferences默认为true】
// 可以通过setAllowCircularReferences(false);设置不支持循环依赖
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
//。。。
// 第四次调用后置处理器,判断是否需要AOP。将未完成的bean存入三级缓存。
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// 初始化bean实例
Object exposedObject = bean;
try {
// 填充属性,即自动注入。内部完成第五,第六次后置处理器调用
//注入属性时就会发现循环依赖的bean没有。
populateBean(beanName, mbd, instanceWrapper);
// 初始化Spring。内部完成第七,第八次后置处理器调用
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
}
}
============================
-
Spring是如何默认支持循环依赖的?
比如说要在A类中注入B,B类中注入A,在Spring生命周期的doGetBean方法会将A放入表示正在创建的集合中。到了doCreateBean方法中,会通过allowCircularReferences属性判断Spring容器是否允许循环依赖,allowCircularReferences属性默认为true,所以Spring容器是默认开启循环依赖的。当判断允许循环依赖时,Spring会将未完成的bean:A存入第三级缓存singletonFactories中。第二次循环创建B注入A时,到doGetBean方法中会调用getSingleton方法判断单例池,也就是第一级缓存singletonObjects中是否有bean:A,如果没有且A正在创建,就会先从二级缓存earlySingletonObjects 中取,二级取不到就从第三级缓存中取出bean移到第二级缓存中,并清除三级缓存中的这个bean。
-
第一次循环时要注入的bean刚开始创建,到了第二次循环才会判断出这个bean正在创建。
-
可以手动调用初始化spring环境的主要方法refresh(),并在此之前使用setAllowCircularReferences(false)关闭循环依赖。
-
也可以通过扩展功能,实现BeanFactoryPostProcessor
接口,获取BeanDefinition对象,设置属性关闭循环依赖。
-
二级缓存earlySingletonObjects 的作用:出现循环依赖时,保存暴露的早期对象,防止工厂重复创建对象,提升性能。
参考:
https://blog.csdn.net/u012098021/article/details/107352463/
-
@Autowired与@Resource有什么区别?
@Autowired
默认使用byType,匹配不到bean时再使用byName,此时的name是属性的名(xml中的byName是根据set方法名)。可以使用@Qualifier()
指定bean的id。默认情况下必须要求依赖对象必须存在,如果要允许null值,可以设置它的required属性为false,如:@Autowired(required=false) 。
@Resource
默认使用byName,当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。可以@Resource(name = / type =)
指定name与type。
@Autowired
是由后置处理器AutowiredAnnotationBeanPostProcessor类解析的
@Resource
是由后置处理器CommonAnnotationBeanPostProcessor类解析的
-
Spring原理(IOC与AOP的理解)?
spring框架最核心的就是ioc和AOP了,其中ioc控制反转是一种设计模式,主要为了降低代码的耦合度,主要使用依赖注入实现。创建对象不再用new,而是通过xml、注解、Javaconfig的方式装配bean到ioc容器中,再通过构造函数方式、setter方法或@Autowired/@Resource注解方式注入到需要依赖的类中,从而实现将类和类之间的依赖关系交给spring容器去管理。
AOP面向切面编程就是将系统性的服务,比如说日志模块,安全模块,事务管理等通过自定义注解、切面类和反射实现切面化,并用声明的方法将他们应用到他们需要影响的组件中去,从而使业务组件具有更高的内聚性,只需关心自身的业务,而不用为了实现系统性的服务导致代码复杂。
-
Spring Bean的生命周期?
-
首先,Java虚拟机将类的class文件加载到方法区,然后扫描是否有注入bean的操作,进行Bean的实例化。【对于要注入的,spring会new一个
BeanDefination
接口(这个接口包括各种对bean的属性判断方法,例如GenericBeanDefinition
)的实现类】
-
实例化后对bean进行属性注入。【然后将实现类对象即bean的各种属性(属性例如:beanClassName=“A”, beanClass=A.class,scope=“singleton”,…)put到
BeanDefinitionMap
的beanDefinitionMap中去,通过finishiBeanFactoryInitialization(beanFactory)方法获取beanDefinitionMap中的beanName开始验证】
- 初始化之前实现BeanNameAware、BeanClassLoaderAware、BeanFactoryAware接口,获取bean的name,类加载器,BeanFactory容器等资源。
- 再实现ApplicationContextAware接口,返回当前的ApplicationContext应用上下文对象。
-
实现BeanPostProcessor接口,bean初始化之前执行postProcessBeforeInitialization()方法。【bean对象到单例池之前还可以调用
BeanFactoryPostProcessor
接口的实现类进行扩展。验证成功后new一个bean对象放到spring单例池SingletonObjects
(map类型)中】
- 实现InitializingBean接口,调用afterPropertiesSet()方法初始化bean。如果bean使用init-method声明了初始化方法,该方法也会被调用
- 实现BeanPostProcessor接口,bean初始化之后执行postProcessAfterInitialization()方法。
- 此时,Bean已初始化完成,可以被应用程序使用。他们将一直驻留在应用上下文中,直到应用上下文被销毁。
- 实现DisposableBean接口,执行destory()方法,销毁bean。如果bean使用了destory-method 声明销毁方法,该方法也会被调用。
【注意:原型对象不是在spring初始化的时候new一个bean,而是在getBean的时候】
【
-
BeanPostProcessor
-
InstantiationAwareBeanPostProcessor
Spring扩展中最重要的两个接口!InstantiationAwareBeanPostProcessor作用于实例化阶段的前后,BeanPostProcessor作用于初始化阶段的前后。】
![](https://img-blog.csdnimg.cn/20201126000646750.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1pNWFFRMjMz,size_16,color_FFFFFF,t_70#pic_center)
【 Aware类型的接口的作用就是让我们能够拿到Spring容器中的一些资源。基本都能够见名知意,Aware之前的名字就是可以拿到什么资源,例如BeanNameAware
可以拿到BeanName,以此类推。调用时机需要注意:所有的Aware方法都是在初始化阶段之前调用的!】
参考:
https://www.jianshu.com/p/1dec08d290c1
https://www.cnblogs.com/javazhiyin/p/10905294.html