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中主要由两个核心接口来代表:
-
BeanFactory:
-
这是最基础、最底层的IoC容器,提供了IoC的核心能力,即Bean的实例化、依赖注入和管理。
-
它采用懒加载(Lazy Loading)的策略,只有当客户端代码通过
getBean()方法请求某个Bean时,它才会去创建这个Bean的实例。
-
-
ApplicationContext:
-
它是
BeanFactory的子接口,也是我们现在实际开发中几乎唯一使用的IoC容器。 -
它在
BeanFactory的基础之上,提供了更多、更强大的企业级功能,比如:-
更方便的配置文件加载方式。
-
支持国际化(i18n)。
-
统一的事件发布与监听机制。
-
AOP(面向切面编程)的集成。
-
Web环境的集成等等。
-
-
与
BeanFactory不同,ApplicationContext默认采用饿汉式加载(Eager Loading)的策略。它会在容器启动时,就将所有非懒加载的单例(Singleton)Bean全部实例化并初始化好。
-
Spring IoC容器的实现思路,可以概括为一套非常精密的、基于反射和工厂模式的Bean生命周期管理流程。这个流程大致可以分为以下几个关键步骤:
步骤一:容器启动与配置元信息加载
-
程序启动,首先会创建一个
ApplicationContext的实例(例如AnnotationConfigApplicationContext)。 -
在构造过程中,容器会读取指定的配置元信息。这些信息可以来自XML配置文件、基于Java的配置类(
@Configuration注解)或者通过组件扫描(@ComponentScan)来发现。
步骤二:Bean定义的解析与注册
-
容器会启动一个扫描器来解析加载到的配置信息。它会扫描指定路径下的所有类,找出被
@Component、@Service、@Repository、@Controller等注解标记的类。 -
对于每一个符合条件的类,Spring并不会立即创建它的实例,而是会将其解析成一个叫做
BeanDefinition的对象。 -
BeanDefinition可以理解为是创建Bean的配方或蓝图,它包含了这个Bean的所有元数据,比如类的全限定名、作用域(Singleton/Prototype)、构造函数参数、需要注入的属性、初始化方法等等。 -
所有解析出的
BeanDefinition对象,都会被注册到一个内部的、类型为Map<String, BeanDefinition>的集合中,这个集合通常被称为beanDefinitionMap。
步骤三:Bean的实例化与初始化(Bean的生命周期)
这是整个IoC实现最核心的部分。在ApplicationContext中,容器会遍历beanDefinitionMap,开始实例化所有单例Bean。
这个过程大致如下:
-
实例化 (Instantiation):容器根据
BeanDefinition中的类信息,通过Java的反射机制(Constructor.newInstance())来创建一个Bean的原始对象实例。 -
填充属性 (Populate Properties):也就是依赖注入。容器会检查
BeanDefinition中定义的依赖关系(比如带有@Autowired注解的字段或方法),然后从容器中获取被依赖的Bean(如果被依赖的Bean还没创建,会先去创建它),再通过反射将依赖注入到刚刚创建的Bean实例中。 -
Aware接口回调:如果Bean实现了各种
Aware接口(如BeanNameAware,ApplicationContextAware),Spring会调用这些接口的方法,将相应的资源(如Bean的名字、容器实例等)传递给Bean。 -
BeanPostProcessor前置处理:在Bean的初始化方法被调用之前,容器会调用所有已注册的
BeanPostProcessor的postProcessBeforeInitialization方法,允许我们对Bean进行自定义的修改。 -
初始化 (Initialization):容器会调用Bean自身的初始化逻辑。这主要有两种方式:一种是执行
InitializingBean接口的afterPropertiesSet方法;另一种是执行在@Bean注解或XML中指定的init-method。 -
BeanPostProcessor后置处理:在Bean初始化完成之后,容器会调用所有
BeanPostProcessor的postProcessAfterInitialization方法。这一步非常关键,Spring的AOP功能就是通过这个步骤实现的。如果一个Bean需要被AOP代理,那么这个后置处理器就会返回一个该Bean的代理对象,而不是原始对象。 -
注册到单例池:经过以上所有步骤后,一个完整、可用的Bean就诞生了。它(或者它的代理对象)会被放入到一个单例缓存池(
singletonObjects)中,以供后续的注入或程序调用。
通过这样一套严谨的生命周期管理流程,Spring IoC容器精确地控制了所有对象的创建、依赖关系的注入以及初始化,完美地实现了控制反转。