(造轮子)手写Spring框架-应用上下文

121 阅读3分钟

应用上下文

代码地址:WangChao-ly/chao-spring at application-context (github.com)

建议订阅博主专栏,从下到上系统手写spring源码,体会其中过程!

应用上下文ApplicationContext是spring中较之于BeanFactory更为先进的IOC容器,ApplicationContext除了拥有BeanFactory的所有功能外,还支持特殊类型bean如上一节中的BeanFactoryPostProcessor和BeanPostProcessor的自动识别、资源加载、容器事件和监听器、国际化支持、单例bean自动初始化等。

BeanFactory是spring的基础设施,面向spring本身;而ApplicationContext面向spring的使用者,应用场合使用ApplicationContext。

具体实现查看AbstractApplicationContext#refresh方法即可。注意BeanFactoryPostProcessor和BeanPostProcessor的自动识别,这样就可以在xml文件中配置二者而不需要像上一节一样手动添加到容器中了。

结构说明:

image.png

ApplicationContext

  1. 接口层
  • 顶层接口ApplicationContext继承了ListableBeanFactory, HierarchicalBeanFactory, ResourceLoader等接口,该类拥有了资源加载和BeanFactory的功能。
public interface ApplicationContext extends ListableBeanFactory, HierarchicalBeanFactory, ResourceLoader {
}
  • ConfigurableApplicationContext:该接口继承了ApplicationContext,并提供了refresh()方法,在方法在xml读取后调用,用来初始化容器,做为容器的入口。
public interface ConfigurableApplicationContext extends ApplicationContext{
    /**
     * 刷新容器
     * @throws BeansException
     */
    void refresh() throws BeansException;
}
  1. 实现类
  • AbstractApplicationContext:抽象应用上下文,该类的功能主要是提供BeanFactoryPostProcessor注册BeanPostProcessor的处理方案,并且对外提供一些获取Bean的方法(原则上是调用beanFactory的相关方法),同时提供了抽象方法用来加载资源和具体的BeanFactory初始化等操作用子类来实现。
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
    @Override
    public void refresh() throws BeansException {
        //创建BeanFactory,并加载BeanDefinition
        refreshBeanFactory();
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();

        invokeBeanFactoryPostProcessors(beanFactory);

        registerBeanPostProcessors(beanFactory);

        beanFactory.preInstantiateSingletons();
    }

    /**
     * 创建BeanFactory,并加载BeanDefinition,子类会调用之前写好的类来加载资源和实现注入
     * @throws BeansException
     */
    protected abstract void refreshBeanFactory() throws BeansException;


    /**
     * 从容器中获取BeanFactoryPostProcessor,实例化bean之前调用方法
     * @param beanFactory
     */
    protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory){
        Map<String, BeanFactoryPostProcessor> beanOfType = beanFactory.getBeanOfType(BeanFactoryPostProcessor.class);
        for(BeanFactoryPostProcessor beanFactoryPostProcessor:beanOfType.values()){
            beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
        }
    }

    /**
     * 注册BeanPostProcessor
     * @param beanFactory
     */
    protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
        Map<String, BeanPostProcessor> beanPostProcessorMap = beanFactory.getBeanOfType(BeanPostProcessor.class);
        for (BeanPostProcessor beanPostProcessor : beanPostProcessorMap.values()) {
            beanFactory.addBeanPostProcessor(beanPostProcessor);
        }
    }

    @Override
    public Object getBean(String name) throws BeansException {
        return getBeanFactory().getBean(name);
    }

    @Override
    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
        return getBeanFactory().getBean(name,requiredType);
    }

    @Override
    public <T> Map<String, T> getBeanOfType(Class<T> type) throws BeansException {
        return getBeanFactory().getBeanOfType(type);
    }

    @Override
    public String[] getBeanDefinitionNames() {
        return getBeanFactory().getBeanDefinitionNames();
    }

    /**
     * 获取BeanFactory,返回为接口
     * @return
     */
    public abstract ConfigurableListableBeanFactory getBeanFactory();
}
  • AbstractRefreshableApplicationContext:该类提供具体的创建BeanFactory方法,和抽象的加载BeanDefinition的方法(这个由子类来实现)。
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext{
    private DefaultListableBeanFactory beanFactory;

    @Override
    protected final void refreshBeanFactory() throws BeansException{
        //和该业务无关的抽象出一个方法
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        loadBeanDefinitions(beanFactory);
        this.beanFactory = beanFactory;
    }

    protected DefaultListableBeanFactory createBeanFactory(){
        return new DefaultListableBeanFactory();
    }

    /**
     * 加载BeanDefinition
     * @param beanFactory
     * @throws BeansException
     */
    protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException;

    @Override
    public DefaultListableBeanFactory getBeanFactory(){
        return beanFactory;
    }
}
  • AbstractXmlApplicationContext:该类继承于AbstractRefreshableApplicationContext,实现了资源加载的具体解决方案,引入了XmlBeanDefinitionReader,该类是之前讲过的用来读取xml文件,并且加载到容器中的类,同时提供抽象方法getConfigurations,这个也由子类来实现。
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableApplicationContext{
    @Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory){
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory,this);
        String[] configurations = getConfigurations();
        if(configurations!=null){
            beanDefinitionReader.loadBeanDefinition(configurations);
        }
    }

    /**
     * 获取配置路径
     * @return
     */
    protected abstract String[] getConfigurations();
}
  • ClassPathXmlApplicationContext:该类是对外使用的类,只需要用户传入xml文件地址即可,程序会配置configurations并且调用refresh方案,从而容器就能正常走通了。
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext{
    private String[] configurations;

    public ClassPathXmlApplicationContext(String configuration) throws BeansException{
        this(new String[]{configuration});
    }

    public ClassPathXmlApplicationContext(String[] configurations) throws BeansException{
        this.configurations = configurations;
        refresh();
    }

    @Override
    protected String[] getConfigurations() {
        return this.configurations;
    }
}

总结: 从源码书写方案学习下来,感觉非常优美,接口负责将方法进行规范且对外引用。类之间功能层次分明,层层调用,如果是需要方法可以先调用抽象方法,然后具体的实现让子类来实现,对外最终继承所有功能于ClassPathXmlApplicationContext这个类。

感悟:这里可以用一个化妆来比喻

开始一个人,它没有任何装饰,一个通用标准指示他要去戴帽子(实现了通用接口),这时候他写一个戴帽子的抽象方法,让帽子间类去继承这个人,然后实现戴帽子方法,这样人就戴好了帽子,对外只要调用帽子这个类就行。