江都区城乡建设局门户网站,163网易企业邮箱入口,海洋优质的网站建设,濮阳新闻综合频道回看前言什么是循环依赖呢#xff1f;我们抛开Spring这个框架来聊下什么是循环依赖#xff0c;循环依赖可能在我们平时的开发过程中是属于比较常见的。Spring容器最大的功能就是对bean的生命周期进行管理#xff0c;每个bean在创建的过程中#xff0c;需要得到一个完整的bean需…前言什么是循环依赖呢我们抛开Spring这个框架来聊下什么是循环依赖循环依赖可能在我们平时的开发过程中是属于比较常见的。Spring容器最大的功能就是对bean的生命周期进行管理每个bean在创建的过程中需要得到一个完整的bean需要对bean的所有属性进行赋值如果两个bean出现了相互依赖的情况如果Spring没有处理循环依赖那么出现的结果就是在bean的创建过程中出现相互依赖导致这个bean永远无法创建出来则就导致一直在相互创建那么Spring是如何来解决循环依赖的呢什么情况下会循环依赖1.先看如下demo: B和A相互循环依赖Component
public class B {Autowiredprivate A a;
}Component
public class A {Autowiredprivate B b;
}启动项目结果没有报错。 2.加入异步逻辑修改 Component
public class A {Autowiredprivate B b;Asyncpublic void test(){}
}Component
public class B {Autowiredprivate A a;
}
EnableAsync
public class App {public static void main(String[] args) {ApplicationContext ctx new AnnotationConfigApplicationContext(App.class);}}启动后 解决方案加入lazy注解Component
public class B {AutowiredLazyprivate A a;
}
Component
public class A {Autowiredprivate B b;Asyncpublic void test(){}
}启动后没有异常 上面发现使用Async异步注解循环依赖就会报错有可能是因为有了Async注解修饰的方法其对应的类被代理了那代理了就会报错么我们继续尝试事务注解看看。 Component
public class A {Autowiredprivate B b;Transactionalpublic void test(){}
}Component
public class B {Autowiredprivate A a;
}
EnableTransactionManagement
public class App {public static void main(String[] args) {ApplicationContext ctx new AnnotationConfigApplicationContext(App.class);}}启动后正常没有报错。于是我们不经要问循环依赖本来不会报错为何添加Async异步注解后就会导致报错为何添加Transactional注解就不会报错使用了Async异步注解的循环依赖为何可以使用lazy注解解决我们要想清楚上面的问题就需要了解Bean的生命周期。Bean的生命周期一个简单的Bean生命周期如下 问题出现就属性赋值这里 由图我们知道当B也依赖A时需要去容器中找到AA已经实例化了只是还没属性赋值所以不应该再实例化解决方案在A创建的实例化后用一个map存起来A来不就行了么于是有了二级缓存 似乎上面已经可以解决循环依赖了但细想一下我们会发现问题通过上面的逻辑我们发现了问题所在B赋值属性A时如果从Map中直接获取那么得到的是原生对象如果后续A没有被代理一切没问题如果A被代理了那么B得到的对象就不对了怎么解决如果我们将aop提前是不是解决了问题。 由于A对象的Aop方式提前了那么B依赖的A就是代理对象了A对象执行赋值后后续到Aop这一步会判断是否已经AOP过了是的话就不会再Aop了问题来了:如果C也跟A相互依赖难道C去依赖A时也要通过ObjectFactory获取A的代理对象么如果是这样A就存在2个代理对象了A是单例的因此这样不行于是产生了一个新的缓存我们称之为三级缓存。于是spring似乎完美解决了循环依赖问题但为何使用Async进行异步代理会报错 我们看看报错的原因就知道 那为何Transactional修饰就没问题呢原因是因为ObjectFactory.getObject()方法可以产生代理对象 为何使用lazy注解修饰就能解决问题呢我们看看源码 从源码来看为何Aync注解修饰不能在ObjectFactory.getObject()方法实现代理对象而Tranctional注解相关的处理器那么问题如果A已经在getObject()方法后产生了代理类后续init方法后还会执行代理么答案是不会了因为 总结1、三级缓存各自的作用第一级缓存存的是对外暴露的对象也就是我们应用需要用到的第二级缓存的作用是为了处理循环依赖的对象创建问题里面存的是半成品对象或半成品对象的代理对象第三级缓存的作用处理存在 AOP 循环依赖的对象创建问题能将代理对象提前创建2、Spring 为什么要引入第三级缓存严格来讲第三级缓存并非缺它不可因为可以提前创建代理对象提前创建代理对象只是会节省那么一丢丢内存空间并不会带来性能上的提升但是会破环 Spring 的设计原则Spring 的设计原则是尽可能保证普通对象创建完成之后再生成其 AOP 代理尽可能延迟代理对象的生成所以 Spring 用了第三级缓存既维持了设计原则又处理了循环依赖牺牲那么一丢丢内存空间是愿意接受的