前言
Spring源码学习笔记,Spring-Ioc源码浅析,错误之处欢迎指正,共同学习
介绍
Ioc 容器的核心是控制对象的创建,销毁,管理对象之间的依赖关系,并由IOC容器完成对象的注入。我们只需要关注业务逻辑。
IOC 原理
Ioc 底层通过 java 反射创建实例,利用set方法对实例的依赖进行注入。
搭建源码研究环境
- 直接从 spirng 的 github 上 clone spring-framework 源码,部署项目
- debug 启动该项目,运行main方法
1 | public static void main(String[] args) { |
输出
new Instance......when new Instance......when begin... number : 2 lishq-toSpring test.lishqTest.TestBean@329302
Spring的IOC实现
- 资源(Resource)定位.
- BeanDefinition 的载入和 BeanFactory 的构造.
- 向 IOC 容器(BeanFactory)注册 BeanDefinition.
- 根据 lazy-init 属性初始化 Bean 实例和依赖注入.
IOC中几个重要概念及其关系
1.BeanFactory 2.ApplicationContext 3.BeanDefinition -------------------------------------------------------------------- 1.BeanFactory是IOC容器的接口定义,定义了容器的基本功能 a.获取Bean b.注册Bean c.more 2.ApplicationContext 则是扩展后的容器,它通过继承 MessageSource,ResourceLoader,ApplicationEventPublisher 接口,在BeanFactory 简单IOC容器的基础上添加了许多对高级容器的支持。 a.MessageSource 接口,提供了消息处理的功能。在web项目上用于国际化 b.ResourceLoader 是资源加载接口,用于对不同的Resource进行加载。Resource getResource(String location)通过一个标识加载资源信息。 c.ApplicationEventPublisher 提供了发布 event 组件的接口。spring 的事件体系 和Spring内置事件处理 3.BeanDefinition 可简单理解为容器实现依赖反转功能的核心数据结构 a.Spring 通过定义 BeanDefinition 来管理基于Spring 的应用中的各种对象以及他们直接的相互依赖关系 b.BeanDefinition 抽象了我们对 Bean的定义,是让容器起作用的主要数据类型
Spring Bean 解析过程
Spring Bean 解析过程如上图所示,下面我们据此逻辑debug源码
FileSystemXmlApplicationContext
1 | // FileSystemXmlApplicationContext 的构造方法,默认刷新为true |
AbstractApplicationContext.refresh()
- 构建BeanFactory,以便于产生所需的 Bean
- 注册可能感兴趣的事件
- 创建Bean实例对象
- 触发被监听的事件
1 | // 整个IOC容器初始化的所有逻辑 |
Spring 通过 refresh 操作重建了 ApplicaitonContext,在这个过程中同时也构建了默认的 BeanFactory 以及加载了 BeanDefinition。AbstractApplicationContext类在refresh()方法中调用了obtainFreshBeanFactory, 此方法主要负责重建BeanFactory以及加载BeanDefinition。 下面我们对此方法进行简要分析:
obtainFreshBeanFactory
1 | protected final void refreshBeanFactory() throws BeansException { |
可以看到BeanFactory的创建过程,首先判断是否已存在BeanFactory,如果已存在则销毁:
destroyBeans() 清除Bean引用的缓存
closeBeanFactory() 释放已创建的BeanFactory
其次调用 createBeanFactory 建立默认的 BeanFactoryDefaultListableBeanFactory,也就是 BeanFactory的默认实现;
当BeanFactory建立好之后,进行一项简单的配置 customizeBeanFactory(),以决定在加载 BeanDefinition 的过程中是否允许循环引用以及是否允许对已有BeanDefinition进行覆写。
然后我们看到一个很感兴趣的方法,就是 loadBeanDefinitions(beanFactory),看名字是加载 Definitions,Definition 是核心之一,代表着 IOC 中的基本数据结构。该方法也是个抽象方法,默认实现是 AbstractXmlApplicationContext ,我们看看该方法实现:
loadBeanDefinitions
- XmlBeanDefinitionReader loadBeanDefinitions();
- doLoadBeanDefinitions();
- registerBeanDefinitions();
1 | /** |
我们主要关注该方法的 loadBeanDefinitions(beanDefinitionReader) 就是BeanDefinition的主要加载过程。它使用指定的 XmlBeanDefinitionReader 进行加载BeanDefinition。这个方法仅负责加载或者注册BeanDefinition,而生命周期是通过bean工厂的refreshBeanFactory方法管理
loadBeanDefinitions
1 | protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { |
可以看出,对于BeanDefinition的加载最终是使用 XmlBeanDefinitionReader 的实例进行加载的,此处只是对其指定了加载的资源或者资源的位置。
1 | public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException { |
此方法位于AbstractBeanDefinitionReader类中,主要针对字符串路径进行解析,最终得到Resource对象,然后将Resource交由XmlBeanDefinitionReader进行处理。所以针对 xml 的资源加载的最终位置还是位于XmlBeanDefinitionReader中
1 | // 从指定的资源位置加载bean定义,返回找到的bean定义的数量 |
1 | // 根据输入流加载 Document 文档对象,然后根据得到的文档对象注册到容器 |
可以看到通过 doLoadDocument 方法将其解析为Document对象,然后通过 registerBeanDefinitions 方法对 Document 对象进行提取与 BeanDefinition 的注册.
这里的Document是符合 w3c 定义的标准xml文档; 我们知道xml解析一般有DOM方式(document)、SAX方式(Simple API for XML)以及StAX(Streaming API for xml)方式;
而此处的Resource不会包含太大量的信息,所以采用了DOM方式,即一次将整个XML都加载到内存中进行解析.
registerBeanDefinitions
- DefaultBeanDefinitionDocumentReader
- registerBeanDefinitions();
- doRegisterBeanDefinitions();
- parseBeanDefinitions();
1 | // 解析文档中根层次的元素:“import”、“alias”、“bean” |
解析root的过程,判断root为spring默认标签还是用户自定义标签,分别交由不同委托类解析
1 | private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { |
1 | /** |
首先创建一个 BeanDefinitionHolder,该方法会调用 BeanDefinitionReaderUtils.registerBeanDefinition 方法, 最后执行容器通知事件。
1 | /** |
首先从bean的持有者那里获取了beanName,然后调用 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()), 将bena的名字和 BeanDefinition 注册:
1 | public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) |
注册bean的最后一步,将beanName和 beanDefinition 放进一个 ConcurrentHashMap(256) 中。
那么这个 beanDefinition 是时候创建的呢? 就是在 DefaultBeanDefinitionDocumentReader.processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) 方法中,在这里创建了 BeanDefinitionHolder, 而该实例中解析Bean并将Bean 保存在该对象中。所以称为持有者。
该实例会调用 parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) 方法,该方法用于解析XML文件并创建一个 BeanDefinitionHolder 返回,该方法会调用 parseBeanDefinitionElement(ele, beanName, containingBean) 方法:
1 | public AbstractBeanDefinition parseBeanDefinitionElement( |
我们看看该方法,可以看到,该方法从XML元素中取出 class 元素,然后拿着 className 调用 createBeanDefinition(className, parent) 方法,该方法核心是调用 BeanDefinitionReaderUtils.createBeanDefinition 方法:
1 | protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName) |
该方法很简单,创建一个 Definition 的持有者,然后设置该持有者的Class对象,该对象就是我们在配置文件中配置的Class对象。最后返回。
至此,xml中定义的BeanDefinition元数据被注册至应用上下文中,创建bean工厂,生成Bean定义。但还没有实例化该类。
Spring ioc 依赖注入及lazy-init
我们刚刚创建了Bean工厂,并创建 BeanDefinitions 放进 Map 里,以beanName为key。那么我们现在有了Bean定义,但还没有实例,也没有构建Bean与Bean之间的依赖关系。 接下来我们看如何实例及构建 Bean 依赖关系
1:BeanDefinition 保存所有 bean 定义及依赖关系 2:解析所有 bean 定义,存储到 BeanDefinition 中 3:getBean() 时,触发 ioc 依赖注入 4:getBean() 由使用或者 context 中的 finishBeanFactoryInitialization() 循环触发(判断是否lazy,是否是单例) 5:创建bean自身---由 factoryBean 或者自定义的 facotry-method 或者默认构造方法(cglib或class.newInstance()) 6:ioc 依赖注入: A: - BeanDefinitionValueResolver,判断各种属性类型获取属性值(如果ref引用类型,递归调用getBean(),其它则返回对应值) B: - 正式注入:BeanWrapper.setPropertyValues()
AbstractApplicationContext.refresh().finishBeanFactoryInitialization(beanFactory) 实例化所有的非延迟加载的单例类
preInstantiateSingletons
1 | public void preInstantiateSingletons() throws BeansException { |
doGetBean
1 | protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, |
可以看到,该方法首先会获取依赖关系,拿着依赖的BeanName 递归调用 getBean方法,直到调用 getSingleton 方法返回依赖bean,而 getSingleton 方法的参数是 createBean 返回的实例,该方法内部调用 AbstractAutowireCapableBeanFactory.doCreateBean 方法:
1 | protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) |
核心代码:
- instanceWrapper = createBeanInstance(beanName, mbd, args) 创建实例。
- populateBean(beanName, mbd, instanceWrapper) , 用于填充Bean,发生依赖注入。
createBeanInstance
1 | /** |
该方法首先创建Class 对象,然后获取构造器对象,最后调用 instantiateBean(beanName, mbd)
1 | /** |
该方法核心逻辑是 beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent),携带BeanName,RootBeanDefinition ,发挥的策略对象是 SimpleInstantiationStrategy,该方法内部调用静态方法 BeanUtils.instantiateClass(constructorToUse), 最后调用 Constructor 的 newInstance 方法,
也就是最终使用反射创建了该实例:
1 |
|
到这里,我们的实例已经创建。但是我们的实例的依赖还没有设置:
- populateBean(beanName, mbd, instanceWrapper) , 用于填充Bean,依赖注入。
populateBean
1 | protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { |
该方法核心逻辑是 PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null), 即获取该 bean 的所有属性,也就是我们配置的 property 元素。最后执行 applyPropertyValues(beanName, mbd, bw, pvs) 方法。现在的PropertyValues 都是字符串,没有值,这个方法的作用就是获取值,关键代码:Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue),该方法会获取 pvName 所对应的容器 value,该方法内部会调用 BeanWrapperImpl.resolveReference(argName, ref) 方法
resolveReference
1 | private Object resolveReference(Object argName, RuntimeBeanReference ref) { |
该方法是最后一步,我们看到该方法会找到set方法,然后调用 Method 的 invoke 方法,完成属性注入。
总结
Spring 的 Bean 其实就是 BeanDefinition,在 Bean 的创建和依赖注入的过程中,需要根据 BeanDefinition 的信息来递归的完成依赖注入。这些递归都是以 getBean() 为入口的,一个递归是在上下文体系中查找需要的 Bean 和创建 Bean 的递归调用;另一个 Bean 是在依赖注入时,通过递归调用容器的 getBean 方法,得到当前的依赖 Bean,同时也触发对依赖 Bean 的创建和注入。在对 Bean 的属性进行依赖注入时,解析的过程也是一个递归的过程,这样,根据依赖关系,一层一层的完成 Bean 的创建和注入,直到最后完成当前 Bean 的创建。有了这个顶层 Bean 的创建和对他的属性依赖注入的完成,意味着当前 Bean 相关的整个依赖链的注入也完成了。