什么是循环依赖?Spring如何解决循环依赖?

1. Spring创建代理原理

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

1.1 ProxyFactory类

第一步:创建一个基础SpringBoot项目

<!--web-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- aop-starter -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

第二步:创建一个测试类

package com.lvpt.circular;

/**
 * 创建代理原理
 * @author jgy
 * @date 2022-09-14
 **/
public class App01 {
   

    public static void main(String[] args) {
   
        //aspect(切面) = advice(通知) + pointcut(切点),切面类就是可用抽离出来的公共代码
        //advisor = 更细粒度的切面,包含一个通知和切点
    }

    interface I1{
   
        void foo();
        void bar();
    }

    static class Target1 implements I1{
   

        @Override
       public void foo(){
   
           System.out.println("target1 foo");
       }

       @Override
        public void bar() {
   
            System.out.println("target1 bar");
        }
    }
}

第三步:写一个测试方法,使用ProxyFactory实现增强

/**
 * 代理工厂 proxyFactory测试1
 **/
public void proxyFactoryTest1(){
   
    //1、创建代理工厂
    ProxyFactory proxyFactory = new ProxyFactory();
    //2、指定代理对象
    proxyFactory.setTarget(new Target1());
    //3、添加通知: MethodInterceptor: 方法拦截器,属于环绕通知; 底层还是调的addAdvisor(),
    //用了Pointcut.TRUE: 即匹配所有方法: DefaultPointcutAdvisor(Pointcut.TRUE, new MethodInterceptor)
    proxyFactory.addAdvice((MethodInterceptor) invocation -> {
   
        try {
   
            System.out.println("前置增强....");
            //调用目标
            return invocation.proceed();
        }finally {
   
            System.out.println("后置增强....");
        }
    });
    //我们也可用改变代理方式,设置代理方式为JDK代理: 默认CGLib
    proxyFactory.addInterface(I1.class);
    //这是就需要写成接口类型
    I1 proxy = (I1)proxyFactory.getProxy();
    //强制指定用CGLib: true:CGLib; false:JDK
    //proxyFactory.setProxyTargetClass(true);
    //4、拿到代理对象: CGLib代理方式(默认)
    //Target1 proxy =(Target1) proxyFactory.getProxy();
    //5、调用增强后的方法
    proxy.foo();
    proxy.bar();
    //打印类名,可用知道用的是CGLib代理
    System.out.println("代理方式: " + proxy.getClass().getName());
}

测试结果:都得到增强

前置增强....
target1 foo
后置增强....
前置增强....
target1 bar
后置增强....
com.lvpt.circular.$Proxy0

第四步:再写一个测试方法,添加AspectJExpressionPointcut:切点表达式,只增强指定方法

/**
 * 代理工厂: proxyFactory测试2
 **/
public void proxyFactoryTest2(){
   
    //1、创建代理工厂
    ProxyFactory proxyFactory = new ProxyFactory();
    //2、指定代理对象
    proxyFactory.setTarget(new Target1());

    //3、获取AspectJ切点表达式
    AspectJExpressionPointcut expressionPointcut = new AspectJExpressionPointcut();
    //4、定义切点表达式
    expressionPointcut.setExpression("execution(* foo())");
    //5、创建通知: 使用默认通知DefaultPointcutAdvisor,
    //参数为: 切点表达式 + 通知(这就是advisor,只有一个切面,也就是这两个参数)
    proxyFactory.addAdvisor(new DefaultPointcutAdvisor(expressionPointcut, (MethodInterceptor) invocation -> {
   
        try {
   
            System.out.println("前置增强....");
            //调用目标
            return invocation.proceed();
        }finally {
   
            System.out.println("后置增强....");
        }
    }));
    //再次添加切面Advisor(可用多个)
    proxyFactory.addAdvisor(new DefaultPointcutAdvisor(expressionPointcut, (MethodInterceptor) invocation -> {
   
        try {
   
            System.out.println("前置增强22....");
            //调用目标
            return invocation.proceed
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
源码深度解析是一种深入研究源代码的方法,通过仔细阅读和理解源代码中的细节和逻辑,以获得对代码的深刻理解和洞察。这样的分析可以帮助开发者更好地理解代码的实现方式,从而更好地理解并使用该代码库。 关于spring如何解决循环依赖的问题,我们可以从源码的角度来分析。Spring采用了三级缓存来解决循环依赖的问题。 第一级缓存是singletonFactories缓存,用于存储正在创建的Bean的工厂对象。当容器正在创建一个Bean时,会将这个Bean的工厂对象存储在singletonFactories缓存中。 第二级缓存是earlySingletonObjects缓存,用于存储已经完成了属性填充但尚未初始化完成的Bean。当容器创建一个Bean时,会将正在创建的Bean存储在earlySingletonObjects缓存中。 第三级缓存是singletonObjects缓存,用于存储已经完成初始化的Bean。当一个Bean初始化完成后,会将其存储在singletonObjects缓存中。 Spring在创建Bean的过程中,会先查找一级缓存,如果找到了对应的工厂对象,则直接返回该对象,避免了创建过程中的循环依赖。如果一级缓存中没有找到对应的工厂对象,则通过递归的方式创建依赖的Bean。 在创建Bean的递归过程中,如果发现正在创建的Bean已经在二级缓存中,说明发生了循环依赖。此时,Spring会从二级缓存中获取正在创建的Bean的代理对象,以解决循环依赖。 当一个Bean创建完成后,会将其放入三级缓存中,并从一级缓存和二级缓存中移除。 总结来说,Spring通过三级缓存的方式解决循环依赖的问题,保证了Bean的创建过程中不会陷入无限递归的循环。这种机制的实现使得Spring解决循环依赖问题上具有较好的性能和效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值