IoC

发布时间 2023-07-15 16:27:33作者: 王景迁

IoC(控制反转)

Java中,一个类想要调用另一个类中的属性或方法,先通过new的方式创建后者的对象,再调用其属性或者方法。调用者掌握着被调用者对象创建的控制权。

控制反转
把原本调用者通过直接new的方式来实现对象创建,反转给IoC容器来实现。

在Spring中,IoC容器管理Java对象。
BeanDefinition是spring对java bean的抽象,配置文件可以是xml/json/properties/yaml/注解扫包等任意一种。
1. 通过XML配置文件<bean>标签、注解@Component、Java配置类等方式来配置Java对象。
2. Spring启动时,初始化IoC容器,读取配置文件,将配置文件转换为BeanDefinition,通过Java反射和工厂模式来根据类名生成相应的对象,把它注入到依赖它的对象中。
3. 当需要使用某个Bean时,直接从IoC容器中获取(例如通过ApplicationContext的 getBean方法),不通过手动new的方式来创建。

@Async导致Spring无法解决的循环依赖

@EnableAsync
@Service
public class A implements AInterface {
    @Autowired
    private BInterface b;
    @Async
    @Override
    public void funA() {
    }
}

@Service
public class B implements BInterface {
    @Autowired
    private AInterface a;

    @Override
    public void funB() {
        a.funA();
    }
}

context.getBean(A)开始创建A,A实例化完成后给A的依赖属性b开始赋值
context.getBean(B)开始创建B,B实例化完成后给B的依赖属性a开始赋值
调用A的getEarlyBeanReference方法得到它的早期引用,@Async还没执行,返回原始对象。
B完成初始化、完成属性的赋值,属性field持有的是Bean A原始对象的引用。
完成了A的属性的赋值(已持有B的实例的引用),继续执行初始化方法initializeBean(...),解析@Aysnc注解,生成一个代理对象,所以最终exposedObject是一个代理对象(而非原始对象)最终加入到容器里。
B引用的属性A是个原始对象,准备return的实例A是个代理对象,即B引用的不是最终对象(不是最终放进容器里的对象)。
执行自检程序:由于allowRawInjectionDespiteWrapping默认值是false,表示不允许上面不一致的情况发生,所以报错了。

解决方法
业务上解开这个依赖关系。

参考资料
https://blog.csdn.net/f641385712/article/details/92797058

IoC容器的两种实现

BeanFactory

BeanFactory是IoC容器的基本实现,也是Spring提供的最简单的IoC容器。
BeanFactory采用懒加载机制,只有使用这个对象时才会创建。

ApplicationContext

ApplicationContext是BeanFactory接口的子接口,是对BeanFactory的扩展。ApplicationContext在BeanFactory的基础上增加了许多企业级的功能,例如AOP(面向切面编程)和事务支持等。

查看注入的bean

@SpringBootApplication
public class SpingTestApplication {

    public static void main(String[] args) {
//        SpringApplication.run(SpingTestApplication.class, args);
        ApplicationContext context = SpringApplication.run(SpingTestApplication.class, args);
        Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
    }

}