呵呵,无论从调用关系、方法名称和注释都能敏感的感觉到找对地方了, prepareRefresh 的注释写“ refreshing” ,真搞不清这个概念,先进去看看:
protected void prepareRefresh() { this.startupDate = System.currentTimeMillis(); synchronized (this.activeMonitor) { this.active = true; } if (logger.isInfoEnabled()) { logger.info("Refreshing " + this); } }
发现,就是重置一个标志位(先不要急于对看见的东西着迷,我说的是看到和目标暂时无关的东西),我发现貌似和我找的东西无关,先退到到主程序再说 ( 这只是一个小例子,如果遇到一个大的和查找目的无关的代码,你先大体看看,然后再决定是不是马上退回去,不要在这里浪费时间,迟早还会回来的,这可以帮助你减少陷入代码丛林中的几率 ) ,接下来看 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
发现 obtainFreshBeanFactory() 方法的实现是:
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { refreshBeanFactory(); ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; }
对于 refreshBeanFactory 方法:
protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;
唉?一个抽象方法!谁实现它,反正不是 AbstractApplicationContext! 想退回去?且慢,当你不明确知道一个方法调用在干什么,尤其是抽象方法,你最好先找找它的实现,再想退回去也不迟,因为只要你忽略掉一个部分,就会对后面的代码的理解产生偏差,但是不要太深入了,这样既可以保证不会掉到实现的代码丛林中,也不会失掉什么关键点!幸好 Eclipse 有查看一个类、方法实现关系的工具,请按 F4, 看来有个好工具对学习是有好的帮助的,所以千万不要低估工具的力量!另外你看 ConfigurableListableBeanFactory beanFactory = getBeanFactory(); 只是取一个对象工厂!也是抽象方法,你可以也按我上面和下面介绍的办法去分析,结果就是一个创建对象工厂实例的逻辑,没有加载 XML 的逻辑!这里就不累述了,
发现这样一个实现关系,父类中有 DefaultResourceLoader, 有 Resource 加载的能力吧? XML 配置文件不就是一种资源吗?看来靠近了想要到达的地方了,至少不会太远了,于是你想先进去看看,不过我还是建议你看看这个实现关系中的其他类,做到心里有数,至少知道这个地图,不然你一个劲往里钻,没有记不起来如何出来了,尤其是类多了时候,于是我先看了一下上面这个结构,知道下面还有两个 ApplicationContext 实现, Ok, 先看看这个资源加载器,因为从名字来看更接近我想找的东西!
public class DefaultResourceLoader implements ResourceLoader { private ClassLoader classLoader; public DefaultResourceLoader() { this.classLoader = ClassUtils.getDefaultClassLoader(); } public DefaultResourceLoader(ClassLoader classLoader) { this.classLoader = classLoader; } public void setClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } public ClassLoader getClassLoader() { return (this.classLoader != null ? this.classLoader : ClassUtils.getDefaultClassLoader()); } public Resource getResource(String location) { Assert.notNull(location, "Location must not be null"); if (location.startsWith(CLASSPATH_URL_PREFIX)) { return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); } else { try { URL url = new URL(location); return new UrlResource(url); } catch (MalformedURLException ex) { return getResourceByPath(location); } } } protected Resource getResourceByPath(String path) { return new ClassPathContextResource(path, getClassLoader()); } private static class ClassPathContextResource extends ClassPathResource implements ContextResource { public ClassPathContextResource(String path, ClassLoader classLoader) { super(path, classLoader); } public String getPathWithinContext() { return getPath(); } @Override public Resource createRelative(String relativePath) { String pathToUse = StringUtils.applyRelativePath(getPath(), relativePath); return new ClassPathContextResource(pathToUse, getClassLoader()); } } }
好了,我可以肯定的一点是这里是加载 XML 文件的地方,但且不是解析的地方,为什么?精通 Java 的家伙都知道资源加载这回事,也知道 Resource 接口这样的东西干什么用的,从代码上看只是加载资源而已,好了!到此为止,我必须退回去一步了,幸好我之前浏览了一下类实现的结构图, AbstractApplicationContext 下面有两个 ApplicationContext 实现,当你每次看结构时最好做个笔记,在 ApplicationContext 继承体系中,你也能看到这两个类,这样你就清楚这些类和 FileSystemXmlApplicationContext 相对关系了,也知道和 AbtractApplicationContext 之间是个什么关系了,因为我们在 FileSystemXmlApplicationContext 中发现入口的驱动方法是 Refresh 方法,而 Refresh 方法是在 AbstractApplicationContext 中实现的,而 Refresh 方法调用一个方法来实现加载和解析,而这个解析过程被推迟到子类实现,而 FileSystemXmlApplicationContext 并没有实现,只能是处于它们之间的一个 ApplicationContext 来实现的(有关为什么会有这样一个“复杂”的继承体系 -ApllicationContext 下又有一个 ApplicationContext, 我会慢慢给大家讲清楚,这也和软件的设计思想有关,而且是最朴素的理念!),基于这种分析,我就在 AbstractRefreshableApplicationContext 和 GenericApplicationContext 之间做个抉择!因为 refresh 这个方法名让我联想到可能需要仔细看看 AbstractRefreshableApplicationContext !另外 FileSystemXmlAppliactionContext 的父类中有 AbstractRefreshableApplicationContext ,从地图中分析,从名称中分析(一般像这样著名开源框架都能遵守很好命名规范,否则就不敢拿出来开源了,会被大家骂死)得到最大的可能性是 AbstractRefreshableApplicationContext !我们就试着打开看看吧!
终于发现 refreshBeanFactory 方法实现的地方了:
@Override protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
Very good! 逻辑简单并且明了!前面是如果一个 BeanFactory 存在就摧毁之,然后再创建一个,哈哈,也就是说是实例化 ApplicationContext 时实际上一个重启动作,也就是清空 AppliactionContext 缓存的对象(容器倒出所有的水或者东西),而且你发现重新创建的 DefaultListableBeanFactory, 我猜这个也许是 ApplicationContext 其他行为的核心,比如对象创建,依赖注入和 AOP 等等!再往下看,一下子盯住一个方法那就是 loadBeanDefinitions(beanFactory) ,好了,凡是经常用 Spring 的人,都知道 BeanDefinition 是干什么的,就是里面存有对象依赖关系的元数据,而这些元数据都是在 XML 中定义的,我兴高采烈地进入这个方法来看一看:
protected abstract void loadBeanDefinitions (DefaultListableBeanFactory beanFactory)
throws BeansException, IOException ;
哈哈,又一次触雷,你可能会问,为什么会这么设计,非得子类去实现吗?这个问题是个设计理念的问题,等我把 IOC 的原理都给你展示清楚,你在解释为什么这么设计,会更有说服力的。
现在看来 loadBeanDefinitions 的实现极大可能是在 AbstractRefreshableApplicationContext 和 FileSystemXmlAppliactionContext 之间的 ApplicationContext! 看来记录一下类继承结构很有用啊!而这个之间,就两个类了: AbstractRefreshableConfigApplicationContext 和 AbstractXmlApplicationContext, 范围已近很小了,我们这时大可以仔细看看这两个类了看看哪个才是真实现 loadBeanDefinitions 方法的!检视一下,发现,是在 AbstractXmlApplicationContext 中,
@Override