我们来了解Spring中以XML的方式加载并注册BeanDefinition的过程: 用户代码调用FileSystemXmlApplicationContext
的构造函数, 作为整个流程的入口
public static void main(String[] args){ System.out.println("Hello XmlMain"); String xmlPath = "//Users/wong/Library/IdeaProjects/spring-framework/fellipe/src/main/resources/spring-config.xml"; ApplicationContext applicationContext = new FileSystemXmlApplicationContext(xmlPath); }
FileSystemXmlApplicationContext
类中有两个构造函数. 在实现真正逻辑的构造函数中会调用refresh()
方法, 启动整个容器的加载.
// FileSystemXmlApplicationContext // Invoked by user public FileSystemXmlApplicationContext(String configLocation) throws BeansException { this(new String[] {configLocation}, true, null); } // The contructor that do actual work public FileSystemXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); } }
而 refresh()
的实际实现是在 AbstractApplicationContext
中, 该方法是一个模版方法, 里面调用了由实际具体类来实现的抽象方法. 这里重点关注 obstainFreshBeanFactory()
方法, 它获取了一个真正的IoC容器, 并且在创建该容器后, 会获取之前 ConfigLocations 域的值, 并根据该位置加载对应的XML文件, 并注册相应的 BeanDefinition
// AbstractApplicationContext public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); ... } protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { refreshBeanFactory(); return getBeanFactory(); }
注意到 obstainFreshBeanFactory()
方法中调用的 refreshBeanFactory()
方法 由 AbstractApplicationContext
类的子类AbstractRefreshableApplicationContext
类来实现.
// AbstractRefreshableApplicationContext @Override protected final void refreshBeanFactory() throws BeansException { ... try { DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } ... }
我们关注到该高级容器创建了 DefaultListableBeanFactory
基础容器, 并作为参数传递给 loadBeanDefinitions()
方法, 目的是将解析出来的BeanDefinition 注册到该基础容器中去. 该 loadBeanDefinitions()
方法的实现位于 AbstractXmlApplicationContext .
// AbstractXmlApplicationContext @Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); }
其实, 我们在这里看到的虽然是不同的ApplicationContext ,但其实是因为 FileSystemXmlApplicationContext 类是继承了这些类, 这些类的方法实际上是在同一个 FileSystemXmlApplicationContext 中, 只是由于继承关系, 不同的类负责不同的部分.
如以上代码中所示, 在该方法中创建了一个 XmlBeanDefinitionReader , 并将该 ApplicationContext类的上下文域配置进该 XmlBeanDefinitionReader
中, 然后再次调用重载方法 loadBeanDefinitions()
, 将 XmlBeanDefinitionReader
委派给该方法.
// AbstractXmlApplicationContext protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } }
该方法执行委托类 XmlBeanDefinitionReader
, 这里我们重点关注 configLocations
域的加载. 然后该类调用它的 loadBeanDefinitions
方法, 指定加载 AbstractXmlApplicationContext 中的 configLocations
中的配置信息 ( 其实该 reader 会加载 configResources
与 configLocations
中的配置信息 ). 而该 loadBeanDefinitions
方法具体由 XmlBeanDefinitionReader 的父类 AbstractBeanDefinitionReader 进行实现
// AbstractBeanDefinitionReader @Override public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException { Assert.notNull(locations, "Location array must not be null"); int count = 0; for (String location : locations) { count += loadBeanDefinitions(location); } return count; } @Override public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException { Assert.notNull(locations, "Location array must not be null"); int count = 0; for (String location : locations) { count += loadBeanDefinitions(location); } return count; } @Override public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException { return loadBeanDefinitions(location, null); } public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException { ResourceLoader resourceLoader = getResourceLoader(); ... else { // Can only load single resources by absolute URL. Resource resource = resourceLoader.getResource(location); int count = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location [" + location + "]"); } return count; } }
如以上方法所示, AbstractBeanDefinitionReader 的 loadBeanDefinitions() 方法层层重载, 其实就是从接收多个配置到接收单个配置, 以加载单个配置为执行单位. 我们重点关注 loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources)
方法, 该方法重点关注 else 中的语句, 他获取了 reader 里的 ResourceLoader 其实就是委托人 FileSystemXmlApplicationContext, [[#^17fe57|code snippet]] ( 高级容器因为实现了ResourceLoader , 所以高级容器可以看成是一个 ResourceLoader ). 然后, 该 reader 又将任务委托回给 FileSystemXmlApplicationContext. 而 loadBeanDefinitions(Resource resource)
该方法的具体实现是由 XmlBeanDefinitionReader 具体实现.
// XmlBeanDefinitionReader public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); try { InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } ... } ... } protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { Document doc = doLoadDocument(inputSource, resource); int count = registerBeanDefinitions(doc, resource); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + resource); } return count; } ... } public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
我们先重点关注 return 语句中的 doLoadBeanDefinitions
方法, 该方法真正执行了解析 Resource , 然后从 Resource 中提取出一种 Document (其实就是XML文件封装的类), 然后对 Document 中的 Bean 定义作解析, 注册, 主要调用了方法 createBeanDefinitionDocumentReader
所创建的 DefaultBeanDefinitionDocumentReader 的 registerBeanDefinitions
方法. 如代码中所示, registerBeanDefinitions
方法创建了一个 DocumentReader, 用来解析 Document 中的 BeanDefinition , 然后注册进 Registry 中. 这个 Registry 实际上已经由 createReaderContext(resource) 传进去了.
注册完成的最后, 会前后对比注册前 Registry 中维护 BeanDefinitionMap 的 BeanDefinition 数量与注册后的数量, 得出注册成功的数量. 调用最终返回到 AbstractBeanDefinitionReader 中的 loadBeanDefinitions(String... locations)
代码中去. 再返回到 AbstractXmlApplicationContext 中的 loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
, 再返回到 AbstractRefreshableApplicationContext 中的 refreshBeanFactory()
方法中, 再回到 AbstractApplicationContext 中的 refreshBeanFactory()
方法中, 再返回到该 AbstractApplicationContext 中的 refresh()
方法中.