1.我们重点看看AbstractRefreshableApplicationContext的refreshBeanFactory方法的实现,这个refreshBeanFactory被FileSystemXmlApplicationContext构造函数中的refresh调用。在这个方法中,通过createBeanFactory构建了一个IoC容器供ApplicationContext使用。这个IoC容器就是我们前面提到过的DefaultListableBeanFactory,同时,它启动了loadBeanDefinitions来载入BeanDefinition,这个过程和前面以编程的方式来使用IoC容器(XmlBeanFacoty)的过程非常类似。
1 protected final void refreshBeanFactory() throws BeansException { 2 // 这里判断,如果已经建立了BeanFactory,则销毁并关闭该BeanFactory 3 if (hasBeanFactory()) { 4 destroyBeans(); 5 closeBeanFactory(); 6 } 7 // 这里是创建并设置持有的DefaultListableBeanFactory的地方 8 // 同时调用loadBeanDefinitions载入BeanDefinition的信息 9 try { 10 DefaultListableBeanFactory beanFactory = createBeanFactory(); 11 beanFactory.setSerializationId(getId()); 12 customizeBeanFactory(beanFactory); 13 loadBeanDefinitions(beanFactory); 14 synchronized (this.beanFactoryMonitor) { 15 this.beanFactory = beanFactory; 16 } 17 } 18 catch (IOException ex) { 19 throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); 20 } 21 }
AbstractRefreshableApplicationContext对容器的初始化
1 // 这就是在上下文中创建DefaultListableBeanFactory的地方,而getInternalParentBeanFacoty() 2 // 的具体实现可以 3 // 查看AbstractApplicationContext中的实现,会根据容器已有的双亲IoC容器的信息来生成 4 // DefaultListableBeanFactory的双亲IoC容器 5 protected DefaultListableBeanFactory createBeanFactory() { 6 return new DefaultListableBeanFactory(getInternalParentBeanFactory()); 7 }
createBeanFactory
1 // 这里是使用BeanDefinitionReader载入Bean定义的地方,因为允许有多种载入方式,虽然用得 2 // 最多的事XML定义的形式,这里通过一个抽象函数把具体的实现委托给子类来完成 3 protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) 4 throws BeansException, IOException;
loadBeanDefinitions
1 // 对于取得Resource的具体过程,我们可以看看DefaultResourceLoader是怎样完成的 2 public Resource getResource(String location) { 3 Assert.notNull(location, "Location must not be null"); 4 // 5 if (location.startsWith("/")) { 6 return getResourceByPath(location); 7 } 8 else if (location.startsWith(CLASSPATH_URL_PREFIX)) { 9 return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); 10 } 11 else { 12 try { 13 // Try to parse the location as a URL... 14 URL url = new URL(location); 15 return new UrlResource(url); 16 } 17 catch (MalformedURLException ex) { 18 // No URL -> resolve as resource path. 19 return getResourceByPath(location); 20 } 21 } 22 }
getResource
2.前面我们看到的getResourceByPath会被子类FileSystemXmlApplicationContext实现,这个方法返回的是一个FileSystemResource对象,通过这个对象,Spring可以进行相关的I/O操作,完成BeanDefinition的定位。分析到这里已经一目了然,它实现的就是对path进行解析,然后生成一个FileSystemResource对象并返回,代码清单如下所示。
1 protected Resource getResourceByPath(String path) { 2 if (path != null && path.startsWith("/")) { 3 path = path.substring(1); 4 } 5 return new FileSystemResource(path); 6 }
FileSystemXmlApplicationContext生成FileSystemResource对象
3.如果是其他的ApplicationContext,那么会对应生成其他种类的Resource,比如ClassPathResource、ServletContextResource等。
4.关于Spring中Resource的种类,可以在图1的Resource类的继承关系中了解。作为接口Resource定义了许多与I/O相关的操作,这些操作也都可以从图1种的Resource的接口定义中看到。这些接口对不同的Resource实现代表着不同的意义,是Resource的实现需要考虑的。Resource接口的实现在Spring中的设计如图1所示。
图1 Resource的定义和继承关系
5.从图1中我们可以看到Resource的定位和它的继承关系,通过对前面的实现原理的分析,我们以FileSystemXmlApplicationContext的实现为例子,了解Resource定位问题的解决方案,即以FileSystem方式存在的Resource的定位实现。
6.在BeanDefinition定位完成的基础上,就可以通过返回的Resource对象来进行BeanDefinition的载入了。
7.在定位过程完成以后,为BeanDefinition的载入创造了I/O操作的条件,但是具体的数据还没有开始读入。这些数据的读入将在下面的BeanDefinition的载入和解析中来完成。
8.任然以水桶为例子,这里就像用水桶去打水,要先找到水源。这里完成对Resource的定位,就类似于水源已经找到了,下面就是打水的过程了,类似于把找到的水装到水桶里的过程。找水不简单,但是与打水相比,我们发现打水更需要技巧。