1.概述
Spring中关于依赖注入的代码实现非常丰富,涉及大量类和组件之间的协作与交互。从原理上讲,任何一个框架都存在一条核心执行流程,只要抓住这条主流程,就能把握框架的整体代码结构,Spring也不例外。无论采用何种依赖注入机制,前提都是Spring IoC容器正常启动。因此,IoC容器初始化就是我们理解和把握依赖注入实现机制的前提。
本篇结合Bean的生命周期,把IoC容器初始化过程梳理成两大步骤,即Bean的注册和Bean的实例化。这两个步骤就构成了一条代码主流程。
2 Bean的注册
在使用Spring时,可以通过获取一个应用上下文(ApplicationContext)对象来操作各种Bean,示例代码如下:
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(ModulesWebApplication.class);
这里的ApplicationContext接口代表的就是一个Spring IoC容器,而在Spring中存在一大批ApplicationContext接口的实现类。如果使用注解的配置方式,就可以使用上述代码中的AnnotationConfigApplicationContext来初始化容器上下文对象。刚开始阅读Spring源码时,建议直接从AnnotationConfigApplicationContext的启动流程切入,这一流程位于它的构造函数中,代码如下:
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
this();
//根据注解配置类注册Bean
this.register(componentClasses);
//刷新容器
this.refresh();
}
public AnnotationConfigApplicationContext(String... basePackages) {
this();
//根据包路径配置扫描Bean
this.scan(basePackages);
//刷新容器
this.refresh();
}
这两个构造函数的作用很明确,一个是根据注解配置类注册Bean,另一个则是根据包路径配置扫描Bean。这里以register()方法为例,来讨论Bean的注册过程。该方法源码如下:
public void register(Class<?>... componentClasses) {
Assert.notEmpty(componentClasses, "At least one component class must be specified");
StartupStep registerComponentClass = this.getApplicationStartup().start("spring.context.component-classes.register").tag("classes", () -> {
return Arrays.toString(componentClasses);
});
this.reader.register(componentClasses);
registerComponentClass.end();
}
这里依赖AnnotationBeanDefinitionReader工具类来完成Bean的注册。AnnotationBeanDefinitionReader会遍历所有传入的annotatedClasses注解类,然后通过doRegisterBean()方法完成注册。
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name, @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier, @Nullable BeanDefinitionCustomizer[] customizers) {
//将注解配置类信息转换成一种BeanDefinition
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
if (!this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
abd.setInstanceSupplier(supplier);
//获取Bean的作用域元数据,解析Bean作用域
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
//生成beanName
String beanName = name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry);
//解析AnnotationBeanDefinitionReader中的@Lazy,@Primary等注解
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);