Spring源码学习---IOC容器
前言
在之前的5个文章中我们已经对 IOC, DI, AOP 和配置相关进行了一些了解,相信在此基础上可以帮助大家更好地去阅读 Spring 的源码
源码学习-IOC容器
① 注意事项
1.这里的源码版本号为 version 5.1.3.RELEASE
2.源码获取地址 github.com/spring-proj…
3.jdk要在1.8以上,spring5中大量使用了lambda表达式,而lambda表达式在1.8后开始支持
4.使用指南:spring.io/guides
5.各个版本的介绍:github.com/spring-proj…
6.Spring5.x的版本新特性:github.com/spring-proj…
1.用户配置bean定义 ---> 2.IOC容器加载bean定义
---> 3.IOC容器创建bean实例 ---> 4.使用IOC容器
③ 使用Spring的入口和如何做好阅读源码的准备
使用Spring的入口为:ApplicationContext
ApplicationContext context = new ClassPathXmlApplicationContext(...Service.xml);
前期准备:
创建一个maven工程,仅引入Spring-Context包(注意版本号)即可
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
④ 围绕 ApplicationContext 我们需要了解的问题(会先大致阐述,还并未涉及具体实现)
1. ApplicationContext 就是IOC容器
内部隐藏的BeanFactory是很少被关注的,提供给用户直观看到的就是ApplicationContext,我使用的是IDEA,可以点击类后打开navigate选项---Type Hierarchy---选择SuperTypes Hierarchy,观看它的继承体系,以下是它作为一个接口,又继承了什么接口

既然它继承了上面6个接口,那它必定会有这6个接口相关的行为
2. ApplicationContext 所继承的接口
EnvironmentCapable:取环境相关的参数,.properties文件
ListableBeanFactory:提供BeanFactorys行为
HierarchicalBeanFactory:父子容器
---提供bean分层管理的方式
且父容器无法访问子容器,子容器可以访问父容器,就比如只有儿子问老爸拿钱,没有父亲问儿子要钱的
MethodSource:国际化
ApplicationEventPublisher:应用的事件发布,比如应用的开启,结束,销毁等等
ResourcePatternResolver:加载Resource
这些在后面我们再展开来讲···
3.继承了 ApplicationContext 的接口

那如何先大致地查看这幅图
先看 ConfigurableApplicationContext 这一大块,
ConfigurableApplicationContext 以下全是抽象的,一直到 FileSystemXmlApplicationContext
和 ClassPathXmlApplicationContext 两个xml配置方式的具体实现,
而下面的 GenericApplicationContext 则是通用实现,其中包括了通用xml,静态,动态语言Groovy和注解,
通用xml实现 GenericXmlApplicationContext 支持 FileSystemXmlApplicationContext 和 ClassPathXmlApplicationContext,
则无论放到文件系统或者classpath都可以
两大块 AbstractRefreshableApplicationContext 和 GenericApplicationContext
都继承了 AbstractApplicationContext
一、ConfigurableApplicationContext --- 可配置的 ApplicationContext

作为一个接口肯定加入了某些行为,我们参考图左 structure 处,然后比照源码片段
① void addApplicationListener(ApplicationListener<?> listener);
此处加入了应用监听器,肯定是使用了观察者模式,里面发生的事件都可以往外提供发布
通过这个 listener 就可以获取到
② void addBeanFactoryPostProcessor(BeanFactoryPostProcessor postProcessor);
在手写AOP的时候,postProcessor已经被提及,讲到了beanFactory如何能够灵活扩展,
但是我们当时讲的是BeanPostProcessor,是对bean的创建过程实现阶段动态增强
那为什么现在这个是 BeanFactoryPostProcessor 呢,那就是它把工厂的创建过程也引入了各个阶段
而且提供了各个阶段支持动态增强的功能
ApplicationContext会帮我们完成bean定义的加载,解析等一系列过程,在此过程中我们可能需要灵活加入一些处理
③ void refresh() throws BeansException, IllegalStateException;
这里的refresh方法是刷新,刷新bean定义,IOC容器里面的bean实例
二、AbstractApplicationContext --- 抽象的 ApplicationContext 的实现
此类中已经提供了很多的接口方法的实现,而且里面的定义都普遍具有了数据结构的支持
需要注意的是 registerBeanPostProcessors() 方法是保护类型的,只能供子类调用
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}
之前提及到的 HierarchicalBeanFactory --- 父子容器在这里也有体现
比如它的一个构造器,此时这里父容器就已经给进来了
@Nullable
private ApplicationContext parent;
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
this();
setParent(parent);
}
此时再看看在setParent()方法中,大致就是取得父容器的环境参数,然后进行一个比较 instanceof 与合并 merge 的事情
@Override
public void setParent(@Nullable ApplicationContext parent) {
this.parent = parent;
if (parent != null) {
Environment parentEnvironment = parent.getEnvironment();
if (parentEnvironment instanceof ConfigurableEnvironment) {
getEnvironment().merge((ConfigurableEnvironment) parentEnvironment);
}
}
}
而在此实现后的继续深入扩展就是 AbstractRefreshableApplicationContext 和 AbstractRefreshableConfigApplicationContext , 刚刚在 ConfigurableApplicationContext 的第 ③ 点不是提及了一个 refresh() 方法吗,第一个就是可支持刷新的,第二个就是可支持刷新且又被配置的,再之后就是 xml 的了,我们现在先不细说
三、GenericApplicationContext --- 通用的 ApplicationContext
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry
此时往下看
一、构造方法

不难发现构造方法中,只有默认的 beanFactory 和父容器 parent 等作为参数,并没有提及我们可以给入 beanDefinition 的来源,比如 xml文件等等
private final DefaultListableBeanFactory beanFactory;
// 构造方法:
// 如果你没有传入beanFactory,那就是默认的
public GenericApplicationContext() {
this.beanFactory = new DefaultListableBeanFactory();
}
// 也可以自己提供
public GenericApplicationContext(DefaultListableBeanFactory beanFactory) {
Assert.notNull(beanFactory, "BeanFactory must not be null");
this.beanFactory = beanFactory;
}
和上面一样的套路,提供了setParent()方法
@Override
public void setParent(@Nullable ApplicationContext parent) {
super.setParent(parent);
this.beanFactory.setParentBeanFactory(getInternalParentBeanFactory());
}
二、模板方法 refreshBeanFactory() --- 实现了父类的保护类型的方法
//---------------------------------------------------------------------
// Implementations of AbstractApplicationContext's template methods
//---------------------------------------------------------------------
/**
* Do nothing: We hold a single internal BeanFactory and rely on callers
* to register beans through our public methods (or the BeanFactory's).
* @see #registerBeanDefinition
*/
@Override
protected final void refreshBeanFactory() throws IllegalStateException {
if (!this.refreshed.compareAndSet(false, true)) {
throw new IllegalStateException(
"GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
}
this.beanFactory.setSerializationId(getId());
}
@Override
protected void cancelRefresh(BeansException ex) {
this.beanFactory.setSerializationId(null);
super.cancelRefresh(ex);
}
三、接口 BeanDefinitionRegistry 要求提供的方法,包括实现注册bean定义等等

四、其他

这样下来,我们能大致了解最外层的 ApplciationContext 是如何一步步加入哪些参数的
Finally
此篇只是大致地看了一下 ApplicationContext 的子类所拥有的一些东西,可能大家看起来会觉得一头雾水,下一篇会结合实例去进行使用然后一步步进行分析,可能篇幅就会增加。
有人反映了篇幅过长的问题,所以现在秉承少吃多餐的原则去慢更,望多多总结,互相进步··
转载自:https://juejin.cn/post/6844903860708835342