总览
在使用IOC容器之前,需要定义一个Resource来定位容器BeanDefinition的资源文件,Resource类继承关系如下图所示,参考使用XmlBeanFactory 和DefaultListableBeanFactory两个IOC容器时,均使用了ClassPathRescource作为BeanDefinition数据源,如下所示:
1 | ClassPathResource resource = new ClassPathResource("beans.xml"); |
我们常用的ApplicationContext容器为我们提供了一系列加载不同Resource的功能,比如FileSystemApplicationContext、ClassPathXmlApplicationContext、XmlWebApplicationContext等,下面我们以FileSytemXmlApplicationContext为例,看一看ApplicationContext的Resource定位过程。
FileSystemApplicationContext继承自AbstractXmlApplicationContext,IOC容器的功能由其父类实现,其主要是扩展了从文件系统读取BeanDefinition配置文件的功能,体现在覆盖了DefaultResourceLoader的getResourceByPath方法。
在FileSystemXmlApplicationContext的构造方法中,调用了refresh()方法来启动IOC容器的初始化,这是整个IOC容器初始化的入口,具体的调用的过程如下图所示,下面从源码的角度对这个过程进行分析。
源码分析
1.refresh
refresh()方法在FileSystemXmlApplicationContext的构造方法中调用,这一方法定义在AbstractApplicationContext中,启动了IOC容器的初始化过程,如下所示:
1 | public class FileSystemXmlApplicationContext extends AbstractXmlApplicationContext { |
2.obtainFreshBeanFactory
obtainFreshBeanFactory用于通知子类刷新内部的bean factory,其中调用了refreshBeanFactory方法,这一方法在AbstractApplicationContext中未给出具体实现,留给其子类实现,最终调用的为其子类AbstractRefreshableApplicationContext的refreshBeanFactory方法,代码如下:
1 | public abstract class AbstractApplicationContext extends DefaultResourceLoader implements Configu-rableApplicationContext { |
3.refreshBeanFactory
refreshBeanFactory由AbstractRefreshableApplicationContext类实现,且声明为final方法,不可以被覆盖。在这个方法中,首先判断如果已经创建了beanFactory,则销毁bean并关闭beanFactory,然后创建一个新的DefaultListableBeanFactory作为应用上下文的IOC容器并由当前类对象持有这个beanFactory,同时调用loadBeanDefinitions方法载入BeanDefinition。这里的loadBeanDefinitions是一个抽象方法,留给其子类实现。
1 | public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext{ |
4-5.loadBeanDefinitions
loadBeanDefinitions由AbstractXmlApplicationContext实现,在这个方法中,首先创建了一个XmlBeanDefinitionReader对象,并将这个reader回调给由父类创建的DefaultListableBeanFactory对象,然后对reader进行了一系列配置,最后调用了reader的loadBeanDefinitions方法,代码如下所示:
1 | public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplication-Context { |
注:
1 | beanDefinitionReader.setResourceLoader(this); |
将这个ApplicationContext对象作为了reader的ResourceLoader,根据之前的分析我们能够知道,AbstractXmlApplicationContext间接继承了DefaultResourceLoader,而DefaultResourceLoader又实现了ResourceLoader接口,所以整个继承关系中的ApplicationContext类均为ResourceLoader类型的实例,在最后调用resourceLoader的getResource方法时实则调用了ApplicationContext的getResourceByPath方法,这就是为什么FileSystemXmlApplicationContext重写了getResourceByPath方法就可以实现从文件系统读取XML格式的配置文件。
6.loadBeanDefinitions
这一步调用了XmlBeanDefinitionReader父类AbstractBeanDefinitionReader的loadBeanDefinitions方法,这里有一系列loadBeanDefinitions方法的重载,在最终的调用方法中我们可以看到,对resourceLoader的类型进行了判断,如果resourceLoader是ResourcePatternResolver类型的实例则以通配符模式定义的路径定位资源,否则直接通过路径定位资源。
这里的resourceLoader正是上一步为reader设置的ApplicationContext实例,如下图所示:
AbstractAppliactionContext实现了ResourcePatternResolver接口,作为ResourcePatternResolver类型的resourceLoader实例对象使用,并调用了getResources方法来获得Resource资源对象,代码如下所示:
1 | public abstract class AbstractBeanDefinitionReader implements EnvironmentCapable, BeanDefinitionReader { |
这里调用的getResources方法是定义在ResourcePatternResolver接口中的方法,具体由PathMatchingResourcePatternResolver类实现,在AbstractApplicationContext中持有PathMatchingResourcePatternResolver实例,因此这一过程中调用的getResources实际为PathMatchingResourcePatternResolver对象的getResources方法。
7.getResources
getResources方法由PathMatchingResourcePatternResolver实现,代码如下:
1 | public class PathMatchingResourcePatternResolver implements ResourcePatternResolver { |
8.getResource
最终调用了resourceLoader的getResource方法,并在其中调用了getResourceByPath方法。如下为DefaultResourceLoader的实现,但在FileSystemXmlApplicationContext的实现中,实际上调用了被覆盖的getResourceByPath方法,从而实现了从文件系统读取资源文件的功能。
1 | public class DefaultResourceLoader implements ResourceLoader { |
至此,FileSystemXmlApplicationContext就完成了FileSystemResource的定位工作,有了这个Resource,下一步就可以进行BeanDefinition的载入和注册过程了。
参考资料:《Spring技术内幕》
最后的最后,安利一下自己写的一个Java代码生成工具,能够方便的生成Spring、SpringMVC、Mybatis架构下的Java代码,希望能对大家有所帮助,地址:Java代码生成器:Generator