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://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作用于初始化阶段的前后。】
【 Aware类型的接口的作用就是让我们能够拿到Spring容器中的一些资源。基本都能够见名知意,Aware之前的名字就是可以拿到什么资源,例如
BeanNameAware
可以拿到BeanName,以此类推。调用时机需要注意:所有的Aware方法都是在初始化阶段之前调用的!】参考:
https://www.jianshu.com/p/1dec08d290c1
https://www.cnblogs.com/javazhiyin/p/10905294.html
- 首先,Java虚拟机将类的class文件加载到方法区,然后扫描是否有注入bean的操作,进行Bean的实例化。【对于要注入的,spring会new一个