之前讲了 探索SpringBoot-一起看看Spring核心源码之ApplicationContext(八)
,最后
提到了 ApplicationContext
的 refresh
的三个核心过程分别是Bean发现、读取、注册。今天来进一步分析下源码
。
我们直接定位到 AbstractApplication
的 refresh
函数中的下面这一行。有想要看之前的解析过程的看这篇 探索SpringBoot-一起看看Spring核心源码之ApplicationContext(八)
。
// Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); 复制代码
看注释可以发现,这个方式是告诉子类去 refresh
内部 bean factory
,
我们再进入到 obtainFreshBeanFactory
里面。
/** * Tell the subclass to refresh the internal bean factory. * @return the fresh BeanFactory instance * @see #refreshBeanFactory() * @see #getBeanFactory() */ protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { refreshBeanFactory(); ConfigurableListableBeanFactory beanFactory = getBeanFactory(); if (logger.isDebugEnabled()) { logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory); } return beanFactory; } 复制代码
可以看到,返回值 beanFactory
是由 getBeanFacotry
来获取的。但是,之前进行过一个函数调用, refreshBeanFactory
,这个又是干什么的呢?
按照之前说法, refresh
函数会先经过 Bean
的发现,读取和注册的过程,其实这个 refreshBeanFactory
就是进行 Bean
的发现,读取和注册实现
。让我们继续往下看。
/** * This implementation performs an actual refresh of this context's underlying * bean factory, shutting down the previous bean factory (if any) and * initializing a fresh bean factory for the next phase of the context's lifecycle. */ //大意为对这个context指定的bean factory执行特定的refresh操作。 //如果之前有bean factory则销毁,然后为了下一个context的生命周期中初始化一个新的bean factory @Override protected final void refreshBeanFactory() throws BeansException { //判断是否存在BeanFactory if (hasBeanFactory()) { //销毁Beans destroyBeans(); closeBeanFactory(); } try { //创建一个BeanFactory DefaultListableBeanFactory beanFactory = createBeanFactory(); //设置序列号,方便反序列化 beanFactory.setSerializationId(getId()); //定制BeanFacotory customizeBeanFactory(beanFactory); //发现BeanDefinition loadBeanDefinitions(beanFactory); //将beanFactoryMonitor设置为创建的beanFactory synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } } 复制代码
一切都在注释中了,先认真看一遍注释。可以发现,这里最关键的Bean发现的过程是在 loadBeanDefinitions
中。尝试查看实现,发现这个方法是一个抽象方法,具体实现类会根据不同的来源来实现不同的方式。我们是 XML
的方式,直接看 AbstractXmlApplicationContext
即可。
/** * Loads the bean definitions via an XmlBeanDefinitionReader. * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader * @see #initBeanDefinitionReader * @see #loadBeanDefinitions */ //大意为用XmlBeanDefinitionReader来加载bean definitions @Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. //用给定的BeanFactory来创建一个XmlBeanDefinitionReader XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); //用这个上下文的资源加载环境来配置bean definition reader // Configure the bean definition reader with this context's // resource loading environment. 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. //允许子类对Reader提供特定的初始化,然后执行实际的加载Bean definitions initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); } 复制代码
看注释,然后继续看loadBeanDefinitions(beanDefinitionReader)
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { //注意这里调用了getConfigResources()方法 Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } } 复制代码
注意这里调用了 getConfigResources()
方法,我们在回头看一下 ClassPathApplicationContext
里面的实现。没错,这里 AbstractXmlApplicationContext
其实最终调用的是 ClassPathApplicationContext
里面的实现。这里使用到了模板模式,有不懂模板设计模式的同学,之后我会单独再补习一下。
@Override protected Resource[] getConfigResources() { return this.configResources; } 复制代码
至此,可以认为是 refresh
的 Bean
的发现过程。
明天再分析下, Bean
的读取和注册。
以后这里每天都会写一篇文章,题材不限,内容不限,字数不限。尽量把自己每天的思考都放入其中。
如果这篇文章给你带来了帮助,能请你写下是哪个部分吗?有效的反馈是对我最大的帮助。
我是shane。今天是2019年8月14日。百天写作计划的第二十一天,21/100。