我们来了解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()
方法中.
热门评论
你好,可以关注一下吗