本文使用springframework 5.3.20版本
demo
以java-config形式编写一个测试demo,新建一个 AnnotationConfigApplicationContext,如果是XML形式使用 ClassPathXmlApplicationContext;
两者都继承了 AbstractApplicationContext 类,详细看下面的层次图。
注意:在new AnnotationConfigApplicationContext()时如果未指定参数,会报运行时异常:org.springframework.context.annotation.AnnotationConfigApplicationContext@6ebc05a6 has not been refreshed yet
@Configuration
public class BeanConfig {
@Bean
public User user(){
User user = new User();
user.setUsername("11111");
return user;
}
}
public static void main(String[] args) {
AnnotationConfigApplicationContext cfg = new AnnotationConfigApplicationContext(BeanConfig.class);
User user = cfg.getBean(User.class);
System.out.println(user.getUsername());
}
AnnotationConfigApplicationContext的有参构造执行了3个方法,分别是自己的无参构造、register()、refresh();
在描述前先从网上找了一个总体流程图方便了解一下大致流程,理清思路。
无参构造
在执行AnnotationConfigApplicationContext的无参构造方法前会调用父类GenericApplicationContext的无参构造方法;
GenericApplicationContext中实例化一个DefaultListableBeanFactory,也就是说bean工厂实际上是应用上下文的一个属性;
从上面的类层次图可以看到:应用上下文和bean工厂又同时实现了BeanFactory接口。
-
DefaultListableBeanFactory中的beanDefinitionMap属性用来保存BeanName对应的BeanDefinition对象,beanDefinitionNames属性保存beanName。
- private final Map<String, BeanDefinition> beanDefinitionMap;
- private volatile List beanDefinitionNames;
前面讲到我们为了解IOC使用了Spring提供的AnnotationConfigApplicationContext作为入口展开,那Spring怎么对加了特定注解(如 @Service、@Repository)的类进行读取转化成 BeanDefinition 对象呢?
- BeanDefinition 是 Spring 中极其重要的一个概念,它是一个描述bean的类,它存储了 bean 对象的所有特征信息,如该bean对应的类的全类名是什么,该bean的beanName(就是xml文件中那个id)是否单例,是否懒加载,factoryBeanName 等,我们最终实例化对象,就是跟据这个BeanDefinition中的描述信息进行的。
又如何对指定的包目录进行扫描查找 bean 对象呢?
所以我们需要new一个注解配置读取器和一个路径扫描器。
AnnotatedBeanDefinitionReader中执行了AnnotationConfigUtils中的registerAnnotationConfigProcessors(this.registry)方法,会向容器注册Sprign内置的处理器。
registerAnnotationConfigProcessors方法中通过 new RootBeanDefinition(XX.class) 新建一个RootBeanDefinition(BeanDefinition的一个实现),然后调用registerPostProcessor将内置bean对应的BeanDefinition保存到bean工厂中;
这里需要说明的是:我们刚刚一直在谈到注册bean,实际上就是将内置bean对应的beanDefinition保存到bean工厂中。那为什么要保存beanDefinition呢?因为Spring是跟据beanDefinition中对bean的描述,来实例化对象的,就算自己定义的bean也是要被解析成一个beanDefinition并注册的。
其中最主要的组件便是 ConfigurationClassPostProcessor 和 AutowiredAnnotationBeanPostProcessor ,前者是一个 beanFactory 后置处理器,用来完成 bean 的扫描与注入工作,后者是一个 bean 后置处理器,用来完成 @AutoWired 自动注入。
register
这个步骤主要是用来解析用户传入的 Spring 配置类,解析成一个 BeanDefinition 然后注册到容器中,主要源码如下:
通过生成AnnotatedGenericBeanDefinition,然后解析给BeanDefinition的其他属性赋值,然后将BeanDefinition和beanName封装成一个BeanDefinitionHolder对象注册到bean工厂中(就是将beanName与baenDefinition封装到Map中,将beanName放到list中。Map与list都是bean工厂DefaultListableBeanFactory所维护的属性),和前面内置bean的注册相同。
执行到这一步,register方法到此就结束了,通过断点观察BeanFactory中的beanDefinitionMap属性可以看出: this()和this.register(componentClasses)方法中就是将内置bean和我们传的配置bean的beanDefinition进行了注册,还没处理标记了@Component等注解的自定义bean。