本文是对 tiny-spring
项目的详细解读,聚焦spring-context的基本实现,对应着 (seventh~ninth)-stage
这三个构建过程。
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
的定义也就呼之欲出了:
/** * 在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; } 复制代码
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
。
在上一篇中,我们提到 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)); } } 复制代码
至此一切关节都一打通, test
目录下有一个对 CustomEditorConfigurer
的使用示例。
ApplicationContext
介绍到这里就差不多了,我是爱呆毛的小士郎,欢迎交流~