spring依赖注入的三种方式和循环依赖

spring依赖注入的三种方式

一: 属性注入

就是使用注解@Autowired/@Resource注入

@Controller
public class StudentController {
    //1.属性注入
    @Autowired
    StudentService studentService;
}

优点:使用简单

缺点:

  1. 功能性问题:无法注入一个不可变的对象(final 修饰的对象);
  2. 通用性问题:只能适应于 IoC 容器;

二:setter注入

@Controller
public class StudentController {
    //2.set注入
    private StudentService studentService;
    @Autowired
    public void setStudentService(StudentService studentService) {
        this.studentService = studentService;
    }
}

缺点:

  1. 不能注入不可变对象(final 修饰的对象);
  2. 注入的对象可被修改。因为对外提供了public的set方法

三:构造方法注入

是spring4.x后推荐的注入方式

@Controller
public class StudentController {
    //3.构造方法注入
    private final StudentService studentService;
    @Autowired
    public StudentController(StudentService studentService) {
        this.studentService = studentService;
    }
}
如果当前的类中只有一个构造方法,那么 @Autowired 也可以省略,所以以上代码还可以这样写:

@Controller
public class StudentController {
//3.构造方法注入
private final StudentService studentService;


public StudentController(StudentService studentService) {
this.studentService = studentService;
}
}

优点:

  1 可以注入final类型对象

  2 通用性更好,构造方法注入可以适用任何环境,不需要ioc环境

循环依赖

一级缓存(singletonObjects):存放完全初始化好的Bean,可直接使用。

二级缓存(earlySingletonObjects):存放提前暴露的Bean(已实例化但未完成属性填充和初始化)。

三级缓存(singletonFactories):存放Bean工厂对象(ObjectFactory),用于生成未完成初始化的Bean。

过程: 

创建Bean A: 实例化A:调用构造函数创建A对象(此时属性未填充)。将A的ObjectFactory存入三级缓存。发现A依赖B:尝试从一级缓存获取B,若不存在则开始创建B。

创建Bean B: 调用构造函数创建B对象。将B的ObjectFactory存入三级缓存。发现B依赖A:尝试从一级缓存获取A,未找到。从三级缓存获取A的ObjectFactory并生成A的早期引用(未完成初始化的A对象,也就是二级缓存内的bean)。将A的早期引用注入B:B完成属性填充和初始化,并移入一级缓存。

 完成Bean A的初始化,从一级缓存获取B,A完成属性填充和初始化,移入一级缓存。

注意事项:

  • Spring 只能解决 单例模式(singleton scope)下的、通过 setter / 字段注入的循环依赖

  • 如果使用的是:

    • 构造器注入(Constructor injection) → Spring 无法解决,抛出异常;

    • 原型模式(prototype scope) → Spring 不做缓存,也无法处理循环依赖。

 
posted @ 2024-05-24 11:07  杨吃羊  阅读(641)  评论(0)    收藏  举报