Spring IoC是什么,底层实现思路是什么

IoC的全称是Inversion of Control,即控制反转。它是Spring框架最核心的设计思想,也是整个框架的基石。

要理解控制反转,我们先要看什么是正向控制。在传统的程序开发中,一个对象如果需要依赖另一个对象,通常会由自己来创建和管理这个依赖,比如 private MyService service = new MyServiceImpl();。这里的控制权掌握在对象自己手中。

而IoC则将这个控制权反转了过来。对象不再主动创建和管理它的依赖,而是被动地等待外部环境来向它提供。这个外部环境,在Spring中就是IoC容器。对象只需要声明自己需要哪些依赖,IoC容器就会负责创建这些依赖的实例,并在合适的时机将它们注入到对象中。

所以,IoC还有一个更直观的名字,叫做依赖注入(Dependency Injection, DI)。DI是实现IoC最主要的方式。

IoC的核心目的是为了解耦。通过将对象的创建和依赖关系的管理交给容器,组件之间不再有硬编码的联系,使得整个系统更加模块化,代码也更易于进行单元测试和后期的维护与升级。

Spring的IoC功能是通过一个容器来实现的。这个容器在Spring中主要由两个核心接口来代表:

  1. BeanFactory:

    • 这是最基础、最底层的IoC容器,提供了IoC的核心能力,即Bean的实例化、依赖注入和管理。

    • 它采用懒加载(Lazy Loading)的策略,只有当客户端代码通过getBean()方法请求某个Bean时,它才会去创建这个Bean的实例。

  2. ApplicationContext:

    • 它是BeanFactory的子接口,也是我们现在实际开发中几乎唯一使用的IoC容器。

    • 它在BeanFactory的基础之上,提供了更多、更强大的企业级功能,比如:

      • 更方便的配置文件加载方式。

      • 支持国际化(i18n)。

      • 统一的事件发布与监听机制。

      • AOP(面向切面编程)的集成。

      • Web环境的集成等等。

    • BeanFactory不同,ApplicationContext默认采用饿汉式加载(Eager Loading)的策略。它会在容器启动时,就将所有非懒加载的单例(Singleton)Bean全部实例化并初始化好。

Spring IoC容器的实现思路,可以概括为一套非常精密的、基于反射和工厂模式的Bean生命周期管理流程。这个流程大致可以分为以下几个关键步骤:

步骤一:容器启动与配置元信息加载

  1. 程序启动,首先会创建一个ApplicationContext的实例(例如AnnotationConfigApplicationContext)。

  2. 在构造过程中,容器会读取指定的配置元信息。这些信息可以来自XML配置文件、基于Java的配置类(@Configuration注解)或者通过组件扫描(@ComponentScan)来发现。

步骤二:Bean定义的解析与注册

  1. 容器会启动一个扫描器来解析加载到的配置信息。它会扫描指定路径下的所有类,找出被@Component@Service@Repository@Controller等注解标记的类。

  2. 对于每一个符合条件的类,Spring并不会立即创建它的实例,而是会将其解析成一个叫做BeanDefinition的对象。

  3. BeanDefinition可以理解为是创建Bean的配方或蓝图,它包含了这个Bean的所有元数据,比如类的全限定名、作用域(Singleton/Prototype)、构造函数参数、需要注入的属性、初始化方法等等。

  4. 所有解析出的BeanDefinition对象,都会被注册到一个内部的、类型为Map<String, BeanDefinition>的集合中,这个集合通常被称为beanDefinitionMap

步骤三:Bean的实例化与初始化(Bean的生命周期)

这是整个IoC实现最核心的部分。在ApplicationContext中,容器会遍历beanDefinitionMap,开始实例化所有单例Bean。

这个过程大致如下:

  1. 实例化 (Instantiation):容器根据BeanDefinition中的类信息,通过Java的反射机制(Constructor.newInstance())来创建一个Bean的原始对象实例。

  2. 填充属性 (Populate Properties):也就是依赖注入。容器会检查BeanDefinition中定义的依赖关系(比如带有@Autowired注解的字段或方法),然后从容器中获取被依赖的Bean(如果被依赖的Bean还没创建,会先去创建它),再通过反射将依赖注入到刚刚创建的Bean实例中。

  3. Aware接口回调:如果Bean实现了各种Aware接口(如BeanNameAware, ApplicationContextAware),Spring会调用这些接口的方法,将相应的资源(如Bean的名字、容器实例等)传递给Bean。

  4. BeanPostProcessor前置处理:在Bean的初始化方法被调用之前,容器会调用所有已注册的BeanPostProcessorpostProcessBeforeInitialization方法,允许我们对Bean进行自定义的修改。

  5. 初始化 (Initialization):容器会调用Bean自身的初始化逻辑。这主要有两种方式:一种是执行InitializingBean接口的afterPropertiesSet方法;另一种是执行在@Bean注解或XML中指定的init-method

  6. BeanPostProcessor后置处理:在Bean初始化完成之后,容器会调用所有BeanPostProcessorpostProcessAfterInitialization方法。这一步非常关键,Spring的AOP功能就是通过这个步骤实现的。如果一个Bean需要被AOP代理,那么这个后置处理器就会返回一个该Bean的代理对象,而不是原始对象。

  7. 注册到单例池:经过以上所有步骤后,一个完整、可用的Bean就诞生了。它(或者它的代理对象)会被放入到一个单例缓存池(singletonObjects)中,以供后续的注入或程序调用。

通过这样一套严谨的生命周期管理流程,Spring IoC容器精确地控制了所有对象的创建、依赖关系的注入以及初始化,完美地实现了控制反转。