介绍一下大家常见的一个单词 context 应该怎么去理解,正确的理解它有助于我们学习 spring 以及计算机系统中的其他知识。
context 是什么
我们经常在编程中见到 context 这个单词,当然每个人有每个人的理解,它被理解为:上下文、容器等等。我想说的是,context 理解为上下文最为合适。为什么呢?我以一个在计算机系统的例子来解释一下。
在计算机系统中,进程执行时有进程上下文,如果进程在执行的过程中遇到了中断,CPU 会从用户态切换为内核态(当然这个过程用户进程是感知不到的,由硬件来实现的),此时进程处于的进程上下文会被切换到中断上下文中,从而可以根据中断号去执行相应的中断程序。
通过上面这个例子我们可以发现,进程在执行程序(不管是用户程序,还是内核中的中断程序)时,都会依赖一个上下文,这个上下文由多种数据结构组成,可以提供我们运行时需要的一些数据和保存运行时的一些数据。那其实 context 就可以理解对一个程序运行时所需要的一些数据结构的抽象表达呗。
抽象是个好东西,可以更方便的表达一些东西,更好的设计系统,但大家要想进步也不能停留在抽象层面,要去探索它的真正含义,真正对应的实体。有时间和大家聊一聊抽象应该怎么去理解。
spring context 是什么
回到 spring 中,spring 的 ioc 容器也是程序呀,那它的执行也肯定需要依赖一个上下文。所以大家应该理解 spring context 的意思了吧。
那 spring context 既然是 spring 的上下文,按照我们上面的说法上下文会对应数据结构,那 spring context 的数据结构是什么呢?
换句话说,spring context 究竟包括什么?接下来我就把这个抽象的概念给大家对应到实打实的数据结构上。
spring context 包括什么
主要包括:
- DefaultListableBeanFactory
这就是大家常说的 ioc 容器,它里面有很多 map、list。spring 帮我们创建的 singleton 类型的 bean 就存放在其中一个 map 中。我们定义的监听器(ApplicationListener)也被放到一个 Set 集合中。 - BeanDefinitionRegistry
把一个 BeanDefinition 放到 beanDefinitionMap。 - AnnotatedBeanDefinitionReader
针对 AnnotationConfigApplicationContext 而言。一个 BeanDefinition 读取器。 - 扩展点集合
存放 spring 扩展点(主要是 BeanFactoryPostProcessor、BeanPostProcessor)接口的 list 集合。
spring context 的生命周期
下面大家可以结合代码这段代码去理解 spring context 的生命周期。
public static void main(String[] args) {
// 初始化和启动
AnnotationConfigApplicationContext acaContext = new AnnotationConfigApplicationContext(AppConfig.class);
// 运行
acaContext.getBean(ServiceA.class);
// 关闭/销毁
acaContext.close();
}
初始化和启动
我们平时常说的spring 启动其实就是调用 AbstractApplicationContext#refresh 完成 spring context 的初始化和启动过程。spring context 初始化从开始到最后结束以及启动,这整个过程都在 refresh 这个方法中。refresh 方法刚开始做的是一些 spring context 的准备工作,也就是 spring context 的初始化,比如:创建 BeanFactory、注册 BeanFactoryPostProcessor 等,只有等这些准备工作做好以后才去开始 spring context 的启动。
与现实生活联系一下,你可以把初始化理解为准备原料(对应到编程中就是创建好一些数据结构,并为这些数据结构填充点数据进去),等准备了你才能去真正造玩偶、造东西呀(对应到编程中就是执行算法)。在编程中数据结构与算法是分不开的也是这个道理呀,它们相互依赖并没有严格的界限划分。
运行
spring context 启动后可以提供它的服务的这段时间。
关闭/销毁
不需要用 spring context ,关闭它时,其实对应到代码上就是 acaContext.close();
实现spring-context
前言
对tiny-spring项目的详细解读,聚焦spring-context的基本实现,对应着(seventh~ninth)-stage
这三个构建过程。
引入ResourceLoader
Spring提供的ApplicationContext
在BeanFactory
的基础之上,添加了几个重要特性:资源加载、事件派发和国际化。不仅如此,相较于BeanFactory
只能手动注册服务,ApplicationContext
可以自动发现和注册服务,比如BeanPostProcessor
和BeanFactoryPostProcessor
,并且ApplicationContext
在创建时就会一次性加载所有non lazy init
的JavaBean
,可以说ApplicationContext
将BeanFactory
提高了一个台阶,更易于客户端程序的使用。
让我们先来看看增强的资源加载功能。在第一篇中我们说到Spring提供了一个对资源的统一抽象——Resource
,并由此衍生出多个代表不同来源的Resource
实现。然而,在基础BeanFactory
的使用过程中,具体使用什么类型的Resource
是需要客户端程序手动指定的:
Resource resource = new ClassPathResource("test/config.xml");
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions(resource);
比如这里,我们选择使用ClassPathResource
是通过硬编码实现的,有没有什么办法可以让Spring自动识别资源的类型呢?为了解决这个问题,Spring引入了一个策略接口ResourceLoader
。实际上,Spring还提供了一个ResourceLoader
的子接口ResourcePatternResolver
,它可以一次性加载多个资源。
// 命令行输入 git checkout seventh-stage,在io包中可以看到对ResourceLoader的定义
/**
* 策略接口,用来加载资源。
* Discussion: 可以根据location的不同表现形式,
* 返回不同的Resource,故而称为策略接口。
*/
public interface ResourceLoader {
/**
* 加载一个资源,可以是ClassPathResource/FileSystemResource等等。
*/
Resource getResource(String location);
/**
* 返回ResourceLoader在加载资源时使用的类加载器。
*/
ClassLoader getClassLoader();
}
有了ResourceLoader
,我们只需要提供资源的地址,至于它到底是什么类型的资源Spring会帮我们做判断,而不再需要我们手动指定。DefaultResourceLoader
是一个ResourceLoader
的实现,它根据资源地址中的协议类型来判断具体的资源类型,代码比较简单,这里就不多说了。
引入事件机制
BeanFactory
为其持有的各种JavaBean
提供了一套统一的生命周期管理(init-method
、InitializingBean
,etc),但客户端程序却没有多少手段可以干涉到BeanFactory
本身。为此ApplicationContext
提供了一套事件机制,用以向客户端程序报告ApplicationContext
的进程,当然也支持派发自定义事件。实际上ApplicationContext
还实现了Lifecycle
接口,客户端程序可以依此来开启或关闭ApplicationContext
,不过在我们的实现中并没有提供这一层抽象。
ApplicationContext
中的事件处理是由ApplicationEvent
类和ApplicationListener
接口来提供的,两者分别基于标准的java.util.EventObject
和java.util.EventListener
,事件的派发则是通过ApplicationEventPublisher
接口。Spring中事件派发机制使用观察者模式来实现。
/**
* 应用程序事件派发器,封装了事件的派发逻辑,一般作为
* ApplicationContext的父接口使用。
*/
public interface ApplicationEventPublisher {
/**
* 派发一个ApplicationEvent,并通知所有已注册的ApplicationListener。
*/
void publishEvent(ApplicationEvent event);
}
为了支持ApplicationEventPublisher
的工作,Spring又提供了ApplicationEventMulticaster
接口,它支持对ApplicationListener
的加入、移除管理,还可以将收到的ApplicationEvent
派发到已注册的ApplicationListener
,可以说ApplicationEventMulticaster
就是ApplicationEventPublisher
的一个代理。
/**
* 应用程序事件多播器,管理着一组ApplicationListener,并将
* ApplicationEvent派发给它们。
*
* ApplicationEventPublisher就是利用ApplicationEventMulticaster
* 来将事件派发给监听器。
*/
public interface ApplicationEventMulticaster {
/**
* 添加一个ApplicationListener来接收ApplicationEvent。
*/
void addApplicationListener(ApplicationListener listener);
/**
* 移除一个ApplicationListener。
*/
void removeApplicationListener(ApplicationListener listener);
/**
* 移除所有已注册的ApplicationListener。
*/
void removeAllListeners();
/**
* 将给定的ApplicationEvent多播到适当的ApplicationListener(s)。
*/
void multicastEvent(ApplicationEvent event);
}
DefaultApplicationEventMulticaster
实现了ApplicationEventMulticaster
接口,非常简单的一个类,各位同学可以自行翻阅。
引入ApplicationContext
如果抛开国际化不谈,讲到这里ApplicationContext
的定义也就呼之欲出了:
/**
* 在BeanFactory之上集成许多易用的功能,更加方便客户端的使用,比如资源加载、事件派发等等。
*/
public interface ApplicationContext extends ResourceLoader, ListableBeanFactory, ApplicationEventPublisher {
/**
* 返回ApplicationContext的名称
*/
String getDisplayName();
/**
* 返回ApplicationContext的启动时间
*/
long getStartupDate();
}
和上一篇提到的BeanFactory
的设计思路类似,ApplicationContext
也采用了分层的设计思想,其中一个比较重要的子接口是ConfigurableApplicationContext
:
/**
* 类似于BeanFactory设计,ApplicationContext也只是一个功能最小的接口,
* ConfigurableApplicationContext在此基础之上扩充了它的功能。
*/
public interface ConfigurableApplicationContext extends ApplicationContext {
/**
* 添加一个BeanFactory后置处理器。
*/
void addBeanFactoryPostProcessor(BeanFactoryPostProcessor beanFactoryPostProcessor);
/**
* 添加一个ApplicationListener用来接收ApplicationContext的开启、关闭、刷新等事件。
*/
void addApplicationListener(ApplicationListener listener);
/**
* 刷新ApplicationContext。
*/
void refresh() throws BeansException, IllegalStateException;
/**
* 关闭当前ApplicationContext。
*/
void close();
/**
* 返回当前ApplicationContext内部组合的BeanFactory。
*/
ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;
}
实现ApplicationContext
AbstractApplicationContext
是Spring提供的一个模板类,它实现了ConfigurableApplicationContext
的大部分基础功能。具体的,AbstractApplicationContext
选择继承DefaultResourceLoader
从而获得了资源加载的能力,同时通过ConfigurableApplicationContext.getFactory()
返回的ConfigurableListableBeanFactory
满足了对ListableBeanFactory
的需求。这样的设计,使得AbstractApplicationContext
成功的将BeanFactory
的创建工作留给了子类。
前面说过,ApplicationContext
在启动时会一次性初始化所有non lazy init
的JavaBean
,并且可以自动发现和注册服务,这又是怎么做到的呢?这一切的核心在于refresh()
方法的实现,我们来具体看一下:
// 命令行输入 git checkout eighth-stage
@Override
public void refresh() throws BeansException, IllegalStateException {
// 记录启动时间
startupDate = System.currentTimeMillis();
// 刷新内部BeanFactory
ConfigurableListableBeanFactory beanFactory = refreshBeanFactory();
// 为BeanFactory进行必要的准备工作
prepareBeanFactory(beanFactory);
try {
// 进行额外的后置处理
postProcessBeanFactory(beanFactory);
// 执行BeanFactoryPostProcessor的回调
invokeBeanFactoryPostProcessors(beanFactory);
// 注册所有BeanPostProcessor
registerBeanPostProcessors(beanFactory);
// 初始化事件多播器
initApplicationEventMulticaster();
// 准备完成,正在刷新
onRefresh();
// 注册所有ApplicationListener
registerApplicationListeners();
// 完成BeanFactory的初始化流程
finishBeanFactoryInitialization(beanFactory);
// 完成刷新
finishRefresh();
} catch (BeansException e) {
// 若失败,就清理资源
beanFactory.destroySingletons();
// 抛出这个异常给调用者
throw e;
}
}
其中,refreshBeanFactory()
是一个模板方法,显然我们应该在这里重新创建BeanFactory
并加载配置文件,又因为refresh
方法可以多次调用,所以我们也应该清理一下原来BeanFactory
持有的资源(主要是缓存的singleton bean)。同时为了给子类留下进一步的定制空间,又定义了钩子方法prepareBeanFactory(...)
和postProcessBeanFactory(...)
。提一下与ApplicationContext
有关的几个Aware
接口(ResourceLoaderAware
,ApplicationContextAware
,ApplicationEventPublisherAware
),它们由名为ApplicationContextAwareProcessor
的BeanPostProcessor
来负责处理,这个BeanPostProcessor
就是在prepareBeanFactory(...)
中注册的。
public class ApplicationContextAwareProcessor implements BeanPostProcessor {
private final ApplicationContext applicationContext;
public ApplicationContextAwareProcessor(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException {
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String name) throws BeansException {
return bean;
}
}
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// 添加Bean后置处理器
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
}
到这里,可以说BeanFactory
已经完全准备完毕,是时候为BeanFactoryPostProcessor
执行回调了。
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
// 1. 处理直接注册的BeanFactoryPostProcessor
for (BeanFactoryPostProcessor processor : getBeanFactoryPostProcessors()) {
processor.postProcessBeanFactory(beanFactory);
}
// 2. 处理在配置文件中配置的BeanFactoryPostProcessor
// 2.1 获取类型为BeanFactoryPostProcessor的所有beanName
String[] beanNames = beanFactory.getBeanDefinitionNames(BeanFactoryPostProcessor.class);
for (String beanName : beanNames) {
// 2.2 在调用之前确保BeanFactoryPostProcessor得到初始化
BeanFactoryPostProcessor processor = getBean(beanName, BeanFactoryPostProcessor.class);
// 2.3 调用BeanFactoryPostProcessor的相关方法
processor.postProcessBeanFactory(beanFactory);
}
}
这里分为两部分,首先是为直接注册的BeanFactoryPostProcessor
执行回调,然后再去配置文件中寻找BeanFactoryPostProcessor
,为它们执行回调,也就是所谓的自动发现服务。registerBeanPostProcessors(...)
同理,它在配置文件寻找BeanPostProcessor
,并将寻找到的BeanPostProcessor
注册进BeanFactory
:
protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// ApplicationContext表面上只是一个ListableBeanFactory,
// 并不具备ConfigurableBeanFactory.addBeanPostProcessor(...)的能力。
// 对ApplicationContext来说,配置文件中配置的BeanPostProcessor就是所有的了
String[] beanNames = beanFactory.getBeanDefinitionNames(BeanPostProcessor.class);
if (beanNames.length > 0) {
for (String beanName : beanNames) {
// 实例化所有BeanPostProcessor
BeanPostProcessor processor = getBean(beanName, BeanPostProcessor.class);
// 注册进beanFactory
beanFactory.addBeanPostProcessor(processor);
}
}
}
注意这里beanFactory.getBeanDefinitionNames(...)
是通过查询BeanFactory
持有的BeanDefinition(s)
来判断,并不会导致任何bean的实例化。换句话说,执行完registerBeanPostProcessors(...)
,除了BeanFactoryPostProcessor
和BeanPostProcessor
被实例化了之外,还没有其他bean被实例化。 NOTE: Spring还额外提供了Ordered
接口来调整BeanFactoryPostProcessor
和BeanPostProcessor
的调用顺序,tiny-spring并没有做这些。
接下来的initApplicationEventMulticaster()
创建了ApplicationEventMulticaster
,它被用来实现ApplicationEventPublisher
。
@Override
public void publishEvent(ApplicationEvent event) {
eventMulticaster.multicastEvent(event);
}
现在只要根据事件找到事件对应的监听器,把它交给ApplicationEventMulticaster
管理,一个完整的观察者模式就实现了。在这里Observable
对应ApplicationEvent
,Observer
对应ApplicationListener
,事件发布则由ApplicationEventMulticaster
来处理。onRefresh
也是一个钩子方法,默认只是一个空的实现。
protected void registerApplicationListeners() {
// 1. 重新注册所有手动注册上去的ApplicationListener
manuallyRegisteredListeners.forEach(eventMulticaster::addApplicationListener);
// 2. 注册配置文件中定义的ApplicationListener
Collection<Object> listeners = getBeansOfType(ApplicationListener.class,true,false).values();
for (Object listener : listeners) {
eventMulticaster.addApplicationListener((ApplicationListener) listener);
}
}
剩下的finishBeanFactoryInitialization(...)
实现了ApplicationContext
在启动时一次性初始化所有non lazy init
的JavaBean
的功能:
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
beanFactory.preInstantiateSingletons();
}
直接调用ConfigurableListableBeanFactory.preInstantiateSingletons()
,简洁明了。finishRefresh()
意味着刷新结束,因此它发出了ContextRefreshedEvent
事件通知上一步注册的所有ApplicationListener
。
protected void finishRefresh() {
publishEvent(new ContextRefreshedEvent(this));
}
除了可以刷新,ApplicationContext
还可以在不需要时被关闭:
@Override
public void close() {
System.out.println("正在关闭" + getDisplayName());
// 销毁所有缓存的singleton bean
getBeanFactory().destroySingletons();
// 发出ContextClosedEvent
publishEvent(new ContextClosedEvent(this));
}
ApplicationContext
在被关闭时会销毁所有的singleton bean,并发出ContextClosedEvent
通知所有的ApplicationListener
。
AbstractRefreshableApplicationContext
是AbstractApplicationContext
的子类,它实现了BeanFactory
的创建和刷新,并将配置文件的加载留给了子类。
@Override
protected ConfigurableListableBeanFactory refreshBeanFactory() {
// 刷新时如果BeanFactory已经存在,首先要进行资源的清理
if (beanFactory != null) {
beanFactory.destroySingletons();
beanFactory = null;
}
// 清理完毕重新创建BeanFactory并加载配置文件
try {
DefaultListableBeanFactory listableBeanFactory = createBeanFactory();
loadBeanDefinitions(listableBeanFactory);
beanFactory = listableBeanFactory;
return listableBeanFactory;
} catch (Exception e) {
throw new ApplicationContextException("刷新BeanFactory失败", e);
}
}
/**
* 创建BeanFactory,可以由子类进一步定制。
*/
protected DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory();
}
/// MARK - Template method
/**
* 将加载BeanDefinition的功能交由子类去实现,
* 子类根据资源的类型,运用不同的加载方式,从而派生出不同的ApplicationContext。
*/
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException;
AbstractXMLApplicationContext
在此基础之上实现了对配置文件的加载但屏蔽了它的来源,ClassPathXMLApplicationContext
交代了配置文件的来源,从而成为一个真正可用的ApplicationContext
。
BeanFactoryPostProcessor示例
在上一篇中,我们提到PropertyEditorRegistrar
,使用它可以向BeanFactory
中注册自定义的PropertyEditor
,现在让我们来实现它。在了解了ApplicationContext
的实现之后,我们知道在为BeanFactoryPostProcessor
执行回调的时候,所有的BeanDefinition
已经加载完毕,但是还没有任何bean得到了初始化,这是一个非常好的时机去更新BeanDefinition
。CustomEditorConfigurer
就利用了这个时机,先注册PropertyEditor
到BeanFactory
中,后续bean的初始化过程就能用上自定义的PropertyEditor
。
// 命令行输入 git checkout ninth-stage
public class CustomEditorConfigurer implements BeanFactoryPostProcessor {
// PropertyEditor注册器
private PropertyEditorRegistrar[] propertyEditorRegistrars;
public void setPropertyEditorRegistrars(PropertyEditorRegistrar[] propertyEditorRegistrars) {
this.propertyEditorRegistrars = propertyEditorRegistrars;
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (propertyEditorRegistrars != null) {
for (PropertyEditorRegistrar registrar : propertyEditorRegistrars) {
beanFactory.addPropertyEditorRegistrar(registrar);
}
}
}
}
为了让PropertyEditorRegistrar
和BeanFactory
产生关联,我们需要修改一下ConfigurableBeanFactory
的定义,增加对PropertyEditorRegistrar
的管理:
/**
* 添加一个PropertyEditorRegistrar。
*/
void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar);
在AbstractBeanFactory
中当然也就需要一个集合去保存PropertyEditorRegistrar
:
// 保存所有的PropertyEditorRegistrar
private final Set<PropertyEditorRegistrar> propertyEditorRegistrars = new HashSet<>();
@Override
public void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar) {
propertyEditorRegistrars.add(registrar);
}
上一篇中也说到属性注入最后是交给了BeanWrapper
去处理,BeanWrapper
因而需要同步BeanFactory
中的PropertyEditor
。因此我们需要修改一下AbstractAutowireCapableBeanFactory
中的同步过程,将与PropertyEditorRegistrar
关联的PropertyEditor
也同步过去。
/**
* 将BeanFactory持有的PropertyEditor同步到BeanWrapper
*/
private void registerPropertyEditors(BeanWrapper wrapper) {
// 1. 注册PropertyEditorRegistrar中的
for (PropertyEditorRegistrar registrar : getPropertyEditorRegistrars()) {
registrar.registerCustomEditors(wrapper);
}
// 2. 同步BeanFactory持有的
Map<Class<?>, PropertyEditor> customEditors = getCustomEditors();
Set<Class<?>> keys = customEditors.keySet();
for (Class<?> type : keys) {
wrapper.registerCustomEditor(type, customEditors.get(type));
}
}