转载

手把手教你实现spring-context

  本文是对 tiny-spring 项目的详细解读,聚焦spring-context的基本实现,对应着 (seventh~ninth)-stage 这三个构建过程。

引入ResourceLoader

  Spring提供的 ApplicationContextBeanFactory 的基础之上,添加了几个重要特性:资源加载、事件派发和国际化。不仅如此,相较于 BeanFactory 只能手动注册服务, ApplicationContext 可以自动发现和注册服务,比如 BeanPostProcessorBeanFactoryPostProcessor ,并且 ApplicationContext 在创建时就会一次性加载所有 non lazy initJavaBean ,可以说 ApplicationContextBeanFactory 提高了一个台阶,更易于客户端程序的使用。

  让我们先来看看增强的资源加载功能。在第一篇中我们说到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-methodInitializingBean ,etc),但客户端程序却没有多少手段可以干涉到 BeanFactory 本身。为此 ApplicationContext 提供了一套事件机制,用以向客户端程序报告 ApplicationContext 的进程,当然也支持派发自定义事件。实际上 ApplicationContext 还实现了 Lifecycle 接口,客户端程序可以依此来开启或关闭 ApplicationContext ,不过在我们的实现中并没有提供这一层抽象。

   ApplicationContext 中的事件处理是由 ApplicationEvent 类和 ApplicationListener 接口来提供的,两者分别基于标准的 java.util.EventObjectjava.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 initJavaBean ,并且可以自动发现和注册服务,这又是怎么做到的呢?这一切的核心在于 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 接口( ResourceLoaderAwareApplicationContextAwareApplicationEventPublisherAware ),它们由名为 ApplicationContextAwareProcessorBeanPostProcessor 来负责处理,这个 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(...) ,除了 BeanFactoryPostProcessorBeanPostProcessor 被实例化了之外,还没有其他bean被实例化。 NOTE: Spring还额外提供了 Ordered 接口来调整 BeanFactoryPostProcessorBeanPostProcessor 的调用顺序, tiny-spring 并没有做这些。

  接下来的 initApplicationEventMulticaster() 创建了 ApplicationEventMulticaster ,它被用来实现 ApplicationEventPublisher

@Override
    public void publishEvent(ApplicationEvent event) {
        eventMulticaster.multicastEvent(event);
    }
复制代码

现在只要根据事件找到事件对应的监听器,把它交给 ApplicationEventMulticaster 管理,一个完整的观察者模式就实现了。在这里 Observable 对应 ApplicationEventObserver 对应 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 initJavaBean 的功能:

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

   AbstractRefreshableApplicationContextAbstractApplicationContext 的子类,它实现了 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得到了初始化,这是一个非常好的时机去更新 BeanDefinitionCustomEditorConfigurer 就利用了这个时机,先注册 PropertyEditorBeanFactory 中,后续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);
                }
            }
        }
    }
复制代码

为了让 PropertyEditorRegistrarBeanFactory 产生关联,我们需要修改一下 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 介绍到这里就差不多了,我是爱呆毛的小士郎,欢迎交流~

原文  https://juejin.im/post/5da181465188254f4d2b797d
正文到此结束
Loading...