最近有时间读了一下Spring的源码,顺便记录一下笔记,加深理解。关于Spring IoC源码分析,网上有一篇文章写得很详细,叫 Spring IOC 容器源码分析 ,大家可以去看看。这篇文章是基于XML的,因为工作中基于注解的方式用得多一些,所以就读了一下Spring IoC注解版本的源代码。为了更方便的分析源码,有时候需要对代码一步一步进行调试,所以我写了一个简单的 spring-ioc-analysis ,放在github上,供大家参考。
在分析Spring IoC源码之前,需要弄清楚一些核心概念。
这个是一个接口,它是用来存储Bean定义的一些信息的,比如ClassName,Scope,init-methon,等等。它的实现有RootBeanDefination,AnnotatedGenericBeanDefinition等。
这是BeanDefination的包装类,用来存储BeanDefinition,name以及aliases等。
BeanFactory就是Bean工厂啦,所有的Bean都有BeanFactory统一创建和管理,Spring提供了一个AbstractBeanFactory,它继承了BeanFactory接口,并且实现了大部分BeanFactory的功能。
AbstractBeanFactory有一个非常重要的方法叫getBean()
public <T> T getBean(String name, Class<T> requiredType) throws BeansException { return doGetBean(name, requiredType, null, false); } 复制代码
它是用来根据名字和类型获取Bean对象的。它获取对象的原理是,如果BeanFactory中存在Bean,则从缓存中取Bean,否则创建并返回该Bean,并且将该Bean添加到缓存中(这里指Singleton类型的Bean)。所以确切的讲,Bean是在什么时候创建的,要看什么时候调用getBean方法获取这个Bean,根据Bean定义的不同,getBean方法会再不同时候被调用。如果Bean实现了BeanFactoryPostProcessor,则在refresh方法中调用invokeBeanFactoryPostProcessors方法时创建Bean,如果实现了BeanPostProcessor方法,则在registerBeanPostProcessors方法中创建Bean,如果都没有实现这些接口,则在默认的finishBeanFactoryInitialization方法中创建Bean,这在后边还会讲到。
AnnotationConfigApplicationContext & BeanFactory & BeanDefinitionRegistry & DefaultListableBeanFactory
这几个类的关系也是很重要的,我们来捋一捋。
AnnotationConfigApplicationContext这个类是基于注解的容器类,它实现了BeanFactory和BeanDefinitionRegistry两个接口,拥有Bean对象的管理和BeanDefinition注册功能。同时这个类拥有一个DefaultListableBeanFactory的对象。
DefaultListableBeanFactory也实现了BeanFactory和BeanDefinitionRegistry接口,拥有Bean对象管理和BeanDefinition注册功能。AnnotationConfigApplicationContext是委托DefaultListableBeanFactory来实现对象管理和BeanDefinition注册的。
介绍完了核心概念,我们来开始分析源码,剖析Spring 容器的加载过程。Spring容器启动过程主要做了两件事情:
我们先来看BeanDefinition的加载过程。
我们创建一个简单Maven工程,在main函数中输入如下代码,就启动了一个基于注解的Spring容器,并从容器中获取helloService对象。
public static void main(String[] args) { AbstractApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); context.registerShutdownHook(); HelloService helloService = context.getBean(HelloService.class); helloService.sayHello(); } 复制代码
之所以要使用AbstractApplicationContext类,是因为我们的HelloService实现了一些BeanPostProcessor这样的接口,需要调用AnnotationConfigApplicationContext对象的registerShutdownHook方法。
现在来分析源码,进入AnnotationConfigApplicationContext的构造函数:
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) { this(); register(annotatedClasses); refresh(); } 复制代码
两个主方法,resister和refresh。
进入register方法,它调用了reader.register方法。
public void register(Class<?>... annotatedClasses) { Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified"); this.reader.register(annotatedClasses); } 复制代码
这个reader是AnnotatedBeanDefinitionReader类的一个实例,在AnnotationConfigApplicationContext的构造函数中创建的。构造函数还创建了一个scanner,用于包扫描。
public AnnotationConfigApplicationContext() { this.reader = new AnnotatedBeanDefinitionReader(this); this.scanner = new ClassPathBeanDefinitionScanner(this); } 复制代码
进入reader的register方法:
public void register(Class<?>... annotatedClasses) { for (Class<?> annotatedClass : annotatedClasses) { registerBean(annotatedClass); } } public void registerBean(Class<?> annotatedClass) { doRegisterBean(annotatedClass, null, null, null); } <T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name, @Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) { // 创建BeanDefinition AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass); // 生成bean name String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry)); AnnotationConfigUtils.processCommonDefinitionAnnotations(abd); // ...省去了其它代码 // 创建BeanDefinitionHolder,它是BeanDefiniton的一个包装类,包含BeanDefiniton对象和它的名字 BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName); // 最后调用registerBeanDefinition BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry); } 复制代码
限于篇幅,我们只分析一些比较重要的源代码,其它的源代码都被我删除了。进入BeanDefinitionReaderUtils.registerBeanDefinition
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); // 调用registry的registerBeanDefinition方法,registry是我们传进来的AnnotationConfigApplicationContext,因为它实现了BeanDefinitionRegistry,所以我们进入AnnotationConfigApplicationContext的registerBeanDefinition方法。 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } } 复制代码
进入AnnotationConfigApplicationContext的registerBeanDefinition方法。好奇怪,怎么没有呢,原来在它的父类GenericApplicationContext中。所以进入
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { // 调用beanFactory的registerBeanDefinition。我们之前讲过,AnnotationConfigApplicationContext是委托DefaultListableBeanFactory来注册BeanDefinition和管理Bean的,所以调用了beanFactory的registerBeanDefinition this.beanFactory.registerBeanDefinition(beanName, beanDefinition); } 复制代码
在GenericApplicationContext的构造函数中,创建了这个beanFactory。
public GenericApplicationContext() { // 前面也讲过,DefaultListableBeanFactory也实现了BeanDefinitionRegistry和BeanFactory接口的。 this.beanFactory = new DefaultListableBeanFactory(); } 复制代码
既然beanFactory是DefaultListableBeanFactory的实例,如果进入DefaultListableBeanFactory的registerBeanDefinition方法咯。
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { //... 省去重复注册代码逻辑以及其它一些逻辑判断, //最终执行以下代码,可以看出,最终是将beanDefinition放到beanDefinitionMap中。所有的beanDefinition都会放到这个map中,以beanName为主键。如果一个beanDefinition有多个别名,那个它会被注册多次。 this.beanDefinitionMap.put(beanName, beanDefinition); } 复制代码
好啦,到这里为止,register方法就分析完成了。但是bean定义全部注册完了吗,还没有。这里只是注册完了配置文件的bean definition。至于我们自己注入的类,或者通过扫描注入的类,在refresh方法中,我们在下一篇分析。
我们今天讲了Spring IoC容器源码分析的上半部分,主要讲了一些核心概念,以及BeanDefinition的加载过程。
在Spring中,所有被用户定义的Bean都会以BeanDefinition的方式注册到BeanFactory中。BeanDefinition相当于Bean的元数据,描述了Bean定义的一些属性。
我们讲了AnnotationConfigApplicationContext,BeanFactory,BeanDefinitionRegistry, DefaultListableBeanFactory。AnnotationConfigApplicationContext实现了BeanFactory,BeanDefinitionRegistry接口,DefaultListableBeanFactory也实现了DefaultListableBeanFactory接口。而AnnotationConfigApplicationContext中BeanDefinition的注册和Bean的管理,是委托给DefaultListableBeanFactory去完成的。
另外我们也分析了BeanDefinition的register过程,也确认了AnnotationConfigApplicationContext注册BeanDefinition确实是委托给DefaultListableBeanFactory来完成的。最终BeanDefinition被存储在一个Map中,以beanName为key,以BeanDefinition对象为值。
我们下一篇会继续讲refresh方法。
所有文章在 Github 上同步,你也可以访问我的个人博客点击查看