首页 > 循环依赖的底层原理(三级缓存)
头像
社交牛逼一号
发布于 2021-09-23 20:31
+ 关注

循环依赖的底层原理(三级缓存)

 @Component("bService")
public class AService {
    //aservice的生命周期 1实例化(new AService)2填充bService属性3填充其他属性4aop5放入单例池
    // 不是所有的对象都叫bean,只有经历了spring的所有步骤才能叫做bean

    @Autowired
    private BService bService;

    public void test(){
        System.out.println(bService);
    }
}

@Component("aService")
public class BService {

    @Autowired
    private AService aService;
}
aservice的生命周期:

 1实例化(new AService)原始对象

2填充bService属性---》从单例池中查找----》找不到----》创建BService

1. 实例化(new BService)
2. 填充aService属性---》从单例池中查找----》找不到----》创建aService
3. 填充其他属性
4. aop
5. 放入单例池  bean对象

3填充其他属性

4aop

5放入单例池  bean对象

这样就是循环依赖,我们就需要使用map来解决这个问题

aservice的生命周期:

 1实例化(new AService)原始对象---》LLMap<aservice,aservice对象>

2填充bService属性---》从单例池中查找----》找不到----》创建BService

1. 实例化(new BService)
2. 填充aService属性---》从单例池中查找----》从LLMap找----》找到aservice原始对象
3. 填充其他属性
4. aop
5. 放入单例池  bean对象

3填充其他属性

4aop

5放入单例池  bean对象

我们使用一个map来存放aservice对象,在2.2的时候从map中找到aservice对象,就打破了这个依赖循环。

 

这样表面上也有一个问题,这个aservice对象看起来并没有赋值,但是其实是会有的,因为我们一直是在引用这个aservice的内存地址,那么它就会执行生命周期的动作。

本来是是原始对象,但是我们要代理对象,这时候我们就得提前进行aop得到代理对象

判断是否出现了循环依赖-----》提前进行AOP

aservice的Bean生命周期:

0 creatingSet.add<'aService'> (正在创建中的bean )
1实例化(new AService)原始对象---》

2填充bService属性---》创建BService

    1. 实例化(new BService)
    2. 填充aService属性---》从单例池中查找----》找不到----》aservice正在创建中?---》aservice出现了循环依赖---》AOP---》aservice代理对象
    3. 填充其他属性
    4. aop 判断是否提提前执行AOP Aservice代理对象
    5. 放入单例池  bean对象

3填充其他属性

4aop

5放入单例池  bean对
判断是否出现了循环依赖-----》提前进行AOP

aservice的Bean生命周期:

0 creatingSet.add<'aService'> (正在创建中的bean )
1实例化(new AService)原始对象---》

2.1填充bService属性---》创建BService

    1. 实例化(new BService)
    2. 填充aService属性---》从单例池中查找----》找不到----》二级缓存找---》找不到---》aservice正在创建中?---》aservice出现了循环依赖---》AOP---》aservice代理对象---》放入二级缓存
    3. 填充其他属性
    4. aop 判断是否提提前执行AOP Aservice代理对象
    5. 放入单例池  bean对象
    
2.2填充cService属性---》创建cService

    1. 实例化(new cService)
    2. 填充aService属性---》从单例池中查找----》找不到---》第二级缓存---》aservice代理对象
    3. 填充其他属性
    4. aop 判断是否提提前执行AOP Aservice代理对象
    5. 放入单例池  bean对象

3填充其他属性

4aop 判断是否提提前执行AOP Aservice代理对象
    4.5earlySingletObjects .get('aservice')--->aservice 代理对象
    

5放入单例池  bean对

6 creting,remove(’aservie‘)

二级缓存:earlySingletObjects Map<BeanName,baen> 也是用来解决bean的单例

一级缓存:单例池 singletonObjects储存对象 Map<beanName,bean>

一级缓存中的对象是经历了所有的生命周期的,二级缓存是没有的,但是两个缓存中的对象是同一个对象,因为是单例对象

AOP的代理逻辑

 

当我们进行aop的时候就得需要有对象的原始对象

判断是否出现了循环依赖-----》提前进行AOP
aservice的Bean生命周期:
0 creatingSet.add<'aService'> (正在创建中的bean )
1实例化(new AService)原始对象---》 LLMap<'aservice',lambda(()->getEarlyBeanReference(beanName,mbd,bean))>
2.1填充bService属性---》从单例池中找--》找不到---》创建BService
    1. 实例化(new BService)
    2. 填充aService属性---》从单例池中查找----》找不到----》二级缓存找---》找不到---》aservice正在创建中?---》aservice出现了循环依赖---》三级缓存通过lambda---》拿到aservice原始对象--》AOP---》aservice代理对象---》放入二级缓存
    3. 填充其他属性
    4. aop 判断是否提提前执行AOP Aservice代理对象
    5. 放入单例池  bean对象
3填充其他属性
4aop 判断是否提提前执行AOP Aservice代理对象
    4.5earlySingletObjects .get('aservice')--->aservice 代理对象
5放入单例池  bean对
6 creting,remove(’aservie‘)

lambda(()->gettarlyBeanReference(beanName,mbd,bean))

beanNanem:名字

mbd:beanDefinition

bean:对象

将这个lambda作为第三级缓存的val,三级缓存存储的是<'对象名',lambda(()->gettarlyBeanReference(对象名,beanDefinition,bean对象))>

三级缓存:用来进行兜底的,存储单例对象。

 

单例池 一级缓存 :保存单例对象 保存经过了完整Bean生命周期的对象

 

二级缓存:出现了循环依赖提前来保存没有经历完整Bean生命周期对象的。

 

大部分情况,都是单例的bean,都会把单例bean存到addSingletonFactory的singletonFaction Map中也就是三级缓存。

一个对象出现了循环依赖都能在三级缓存中找到对象。

在填充出现循环依赖的bean对象时:

从单例池中找-->找不到再到二级缓存中找-->找不到再到3级缓存中找(只要是单例bean就能找到)-->aop-->生成代理对象-->放入二级缓存中.

 

单例池是ConcurrentHashMap。

(ConcurrentHashMap详解:线程安全的HashMap,1.7之前是使用分段式锁,将数据分之后采用了C成一段一段储存,给每一段数据配一把锁,也就是segment,不会扩容,最多支持16个线程并发写。1.8之后采用了CAS和synchroized方式处理并发。CAS确定Key的数组下标,synchroized保证链接点的同步效果。也有红黑树。)

二级缓存中放入的同时,需要在一级缓存中释放,就得保证事物的原子性,所以一级缓存和二级缓存就不不要使用ConcurrentHashMap,因为只需要给它们加锁即可实现。

 

gettarlyBeanReference(beanName,mbd,bean)得到提前bean的引用(原始对象)

提前进行AOP

这里判断是否有提前进入过AOP。

如果提前进行了AOP,就给它存储原始对象,这里判断是否进入AOP的就remove这个对象,看它得到是否是这个bean对象(remove和put不同,remove的返回值就是当前的key对应的value.),如果是那就不再进行aop,表示提前进行了AOP。

 

当两个对象互相依赖的时候,就产生了循环依赖,在不是构造方法的情况下,

1先将正在创建的a对象放入creatingSet中,实例化对象,将原始对象放入singletFactrys<对象名,存放对象名,beanDefinition和对象的lambda表达式>的三级缓存,返回对象。

2填充另一个b对象的属性(

2.1实例化

2.2填充a的属性 从singletonObjects 单例池中找 找不到就到earlysingletonObjects 二级缓存中找 找不到 判断a是否在创建中,出现循环依赖 在三级缓存中拿到a原始对象 aop得到a代理对象 放入二级缓存中(同时在一级缓存中释放)

2.3 填充其他属性

2.4 advicedBeans判断是否提前进行了AOP 得到代理对象

2.5 放入一级缓存 代理池中

3 填充其他属性

4 advicedBeans判断是否提前进行了AOP 得到代理对象

4.1 二级缓存中 earlySingletObjects.get(’a‘) 得到代理对象

5 放入单例池 bean对象

6 cretingSet.remove(’a‘)

是不是可以解决所有循环依赖?

@Component("aService")
public class BService {

    private AService aService;

    public BService(AService aService ){
        this.aService = aService;
    }
}

@Component("bService")
public class AService {
    //aservice的生命周期 1实例化(new AService)2填充bService属性3填充其他属性4aop5放入单例池
    // 不是所有的对象都叫bean,只有经历了spring的所有步骤才能叫做bean
    private BService bService;

    public  AService(BService bService) {
        this.bService = bService;
    }
}

并不能。

@Component("bService")
public class AService {
    //aservice的生命周期 1实例化(new AService)2填充bService属性3填充其他属性4aop5放入单例池
    // 不是所有的对象都叫bean,只有经历了spring的所有步骤才能叫做bean
    private BService bService;

    @Lazy
    public  AService(BService bService) {
        this.bService = bService;
    }
}

@Component("aService")
public class BService {

    private AService aService;

    @Lazy
    public BService(AService aService ){
        this.aService = aService;
    }
}

   public static void main(String[] args) {
        UUService uuService = new UUService();//原始对象
        ProxyFactory proxy = new ProxyFactory();
        proxy.setTarget(uuService);//传入原始对象
        proxy.addAdvice(new MethodBeforeAdvice() {
            public void before(Method method, Object[] objects, Object o) throws Throwable {
                //method表示当前执行的方法,objects是方法的参数,o表示tager类
                System.out.println("执行目标方法调用之前的逻辑");
                method.invoke(o,objects);
            }
        });

        UUService uuService1 = (UUService) proxy.getProxy();
        uuService1.test();
    }
    
执行目标方法调用之前的逻辑
类自带的方法
类自带的方法

在有参构造方法上加上@Lazy注解就可以解决问题。

全部评论

(0) 回帖
加载中...
话题 回帖