手记

Spring IoC 获取并加载 BeanDefinition 详细流程

我们来了解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 会加载 configResourcesconfigLocations 中的配置信息 ). 而该 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;  
	}
}

如以上方法所示, AbstractBeanDefinitionReaderloadBeanDefinitions() 方法层层重载, 其实就是从接收多个配置到接收单个配置, 以加载单个配置为执行单位. 我们重点关注 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 所创建的 DefaultBeanDefinitionDocumentReaderregisterBeanDefinitions 方法. 如代码中所示, registerBeanDefinitions 方法创建了一个 DocumentReader, 用来解析 Document 中的 BeanDefinition , 然后注册进 Registry 中. 这个 Registry 实际上已经由 createReaderContext(resource) 传进去了.
注册完成的最后, 会前后对比注册前 Registry 中维护 BeanDefinitionMapBeanDefinition 数量与注册后的数量, 得出注册成功的数量. 调用最终返回到 AbstractBeanDefinitionReader 中的 loadBeanDefinitions(String... locations) 代码中去. 再返回到 AbstractXmlApplicationContext 中的 loadBeanDefinitions(DefaultListableBeanFactory beanFactory), 再返回到 AbstractRefreshableApplicationContext 中的 refreshBeanFactory() 方法中, 再回到 AbstractApplicationContext 中的 refreshBeanFactory()方法中, 再返回到该 AbstractApplicationContext 中的 refresh() 方法中.

2人推荐
随时随地看视频
慕课网APP

热门评论

你好,可以关注一下吗

查看全部评论