Spring的循环依赖问题

时间:2020-9-7 作者:admin


Spring的循环依赖问题

循环依赖就是,一个组件依赖于另一个组件。类似

@Component
public class A {
    @Autowired
	private B b;
    
    public A(B b) {
        this.b = b;
    }
}

@Component
public class B {
    @Autowired
	private A a;
    
    public A(A a) {
        this.a = a;
    }
}

如果要创建A对象,因为构造函数有B属性,要创建B对象,那么需要将A对象注入。

就造成了循环依赖问题。

Spring的循环依赖问题

(因为如果只有有参构造器,那么没有无参构造器,要创建对象需要传入参数才能完成创建。)

解决的方法很明显:

  1. 添加空构造函数,此时可以把参数式构造函数去掉

    @Component
    public class A {
        @Autowired
    	private B b;
        
        // 添加空构造函数
        public A() {
            
        }
        
        // 可以去掉,也可以保留,反正spring不会用到。
        public A(B b) {
            this.b = b;
        }
    }
    
    @Component
    public class B {
        @Autowired
    	private A a;
     
        // 添加空构造函数
        public B() {
            
        }
        
        // 可以去掉,也可以保留,反正spring不会用到。
        public B(A a) {
            this.a = a;
        }
    }
    
  2. 修改字节码,把构造函数改为不需要传参的构造函数

    我不知道能否通过asm来修改字节码来实现把构造函数修改,但是如果想要修改类定义的构造函数,貌似只能通过修改生成的字节码了。

  3. 直接在属性上面加上@Autowired注解,spring会通过反射来注入对象的。

    @Component
    public class A {
        @Autowired
        private B b;
    }
    
    @Component
    public class B {
        @Autowired
        private A a;
    }
    
    @Configuration
    @ComponentScan(basePackages = "inject.dep2")
    @EnableAspectJAutoProxy(proxyTargetClass = true)
    public class Config {
    }
    
    class ATest {
        ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
    
        @Test
        public void test () {
            A a = (A) context.getBean("a");
            System.out.println(a);
        }
    }
    

    结果是可以注入成功。

    spring采用三级缓存来解决循环依赖的问题,但是我个人感觉好像无需三级缓存,不过三级缓存能把整个过程表现地更清晰罢了。

    个人说明:

    我个人制作的简易框架中,没有用到三级缓存,但是也实现了属性的注入。

    主要是因为,默认情况下,ioc控制的对象是单例的,那么在初始化bean时,

    ​ 比如:A发现有属性B,从IOC的map中获取B对象,没有B对象,可以先调用B对象的无参构造方法创建B对象,然后等到B对象注入属性的时候,从Map中获取A对象注入即可。

    ​ 这样,不就解决了循环依赖的问题吗?

    我个人的实现就是采用的最简单的方式,不过应该有不足之处,Spring采用三级缓存解决这种问题,肯定有好处,而且可以使代码更加清晰。

    不过我当时没考虑循环依赖,但是我个人觉得单例模式下,采用map的IOC容器天然就可以解决循环依赖的问题。

    我个人写的微型框架summer,实现了基本的功能,但是暂时注释比较少,后续我会加上

    如果只是对于基本的IOC容器的实现有兴趣,可以查看我写的这篇博客

  4. 采用setter来解决。对于需要注入的属性无需标记@Autowired,只需要将需要注入的属性写上setter方法,spring就可以注入。

    @Component
    public class A {
        private B b;
    
        public void setB(B b) {
            this.b = b;
        }
    }
    
    @Component
    public class B {
        private A a;
    
        public void setA(A a) {
            this.a = a;
        }
    }
    

以上都是建立在单例模式的基础上,如果采用多例模式,每次都需要创建新的对象的话,可想而知,无法通过ioc容器来实现循环依赖的解决、

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class A {
    @Autowired
    private B b;
}

@Component()
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class B {
    @Autowired
    private A a;
}

测试依旧会报错:询问你是否有循环依赖

Spring的循环依赖问题


以上就是我个人对于spring循环依赖的理解,如果有任何不对的请各位指出,我将悉心查验并改正。

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。