美文网首页
手把手教你实现spring-context

手把手教你实现spring-context

作者: anyoptional | 来源:发表于2019-10-14 14:29 被阅读0次

系列文章

手把手教你实现spring-beans (一)
手把手教你实现spring-beans (二)
手把手教你实现spring-context
手把手教你实现spring-aop (TODO)

前言

  本文是对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介绍到这里就差不多了,我是爱呆毛的小士郎,欢迎交流~

相关文章

网友评论

      本文标题:手把手教你实现spring-context

      本文链接:https://www.haomeiwen.com/subject/rejrmctx.html