ClassPathApplicationContext 和 BeanDefinitionReader
我们上一篇开始有这么一段代码不知道大家还记得不:
public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath*:application-context.xml"); ATest aTest = applicationContext.getBean(ATest.class); aTest.doSomeThing(); }
这段代码中ClassPathApplicationContext将我们的xml加载解析成了beanDefinition对象,并且转换成了最终的bean,我们经过前面的学习已经知道了XmlBeanDefinitionReader是怎样把xml解析成beanDefinition对象的,那么,在上面这段代码运行的时候,这个解析过程是发生在哪里呢?我们一起来看看ClassPathApplicationContext的构造方法:
代码块21 public ClassPathXmlApplicationContext(String configLocation) throws BeansException { // 调用的是下边的这个方法 this(new String[] {configLocation}, true, null); } public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); // 解析配置,此时参数是[classpath*:application-context.xml] setConfigLocations(configLocations); if (refresh) { // 调用下面的refresh方法 refresh(); } }
注意哈:这里我暂时只放refresh的前几行代码,因为其他的还和我们目前讲的没关系
代码块22 @Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 1.加载前的准备工作 prepareRefresh(); // 2.获取一个全新的beanFactory实例 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 3.初始化beanFactory,给它设置各种 prepareBeanFactory(beanFactory);
仅仅放出来了开始的那几行代码,是因为我们目前所说到的,也就和这个第二句有关系,我们直接来看2中的obtainFreshBeanFactory,它也是在AbstractApplicationContext中,
代码块23 protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { // 重点 refreshBeanFactory(); ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; }
重点是这第一行的这个方法,这个方法也是AbstractApplicationContext,但是它是个抽象方法,真正的实现是在AbstractRefreshableApplicationContext中,我们直接来看这里的refreshBeanFactory方法:
代码块24 @Override protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); // 加载BeanDefinition的 loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
上面代码块中只有一句我给了注释,那个其实也是个抽象方法,真正的实现是在子类AbstractXmlApplicationContext中,我们到AbstractXmlApplicationContext中来:
代码块25 @Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // 1.我们前文所说过的XmlBeanDefinitionReader XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // 2.现在是在AbstractXmlApplicationContext,其父类既继承了DefaultResourceLoader的,也 // 实现了ResourcePatternResolver beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // 3.设置验证模型 initBeanDefinitionReader(beanDefinitionReader); // 4.加载Resource loadBeanDefinitions(beanDefinitionReader); }
我们在这里直接就可以看到,ClassPathXmlApplicationContext就是用的XmlBeanDefinitionReader来加载解析Resource的,我们来看看上面代码块中3处的代码
代码块26 protected void initBeanDefinitionReader(XmlBeanDefinitionReader reader) { reader.setValidating(this.validating); }
它调用的是XmlBeanDefinitionReader的内部方法,注意参数中this.validating的值是true,我们跟到这个方法中:
代码块27 public void setValidating(boolean validating) { this.validationMode = (validating ? VALIDATION_AUTO : VALIDATION_NONE); this.namespaceAware = !validating; }
哈哈,这下应该是知道之前代码块7和8那里大量出现的validationMode的值是怎么来的了吧,其实就是在这里给赋值的,表示让程序去发现到底该使用怎样的解析方式。最后我们再来看看代码块25中4处的代码
代码块28 protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } // 实际走的是这里,因为上面的configResources此时是null String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } }
这个方法调用的就是我们前文所说过的XmlBeanDefinitionReader中的方法了,至此,xml怎样解析成BeanDefinition就已经说完,我们把我们到现在为止说到过的东西总结成下面的这张图:
我们可以看到,本篇讲的,其实就是最下面这部分的,下篇我们说从BeanDefinition是怎样变成Bean实例的。