本文是针对Srping的ClassPathXMLApplicationContext来进行源码解析,在本篇博客中将不会讲述spring Xml解析注册代码,因为ApplicationContext是BeanFactory的扩展版本,ApplicationContext的GetBean和xml解析注册BeanDefinition都是用一套代码,如果您是第一次看请先看一下XMLBeanFactory解析和BeanFactory.GetBean源码解析:
- XMLBeanFactory源码解析地址:Srping源码之XMLBeanFactory - --lantao-- - 博客园
- BeanFactory.getBean源码解析地址:Srping源码之BeanFactory.getBean - --lantao-- - 博客园
作者整理了spring-framework 5.x的源码注释,代码已经上传者作者的GitHub了,可以让读者更好的理解,地址:
例如现在有这样一个需求,工程在运行过程中用到的某个设置(例如VAR)是从系统环境变量中取得的,而如果用户没有在系统环境变量中配置这个参数,工程不会工作。这一要求也各种各样的解决办法,在Spring中可以这么做,可以直接修改Spring的源码,例如修改ClassPathXmlApplicationContext.当然,最好的办法是对源码进行扩展,可以自定义类:
- 接下来直接上源码:
-
package lantao; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.lantao.UserBean; public class ApplicationContextTest { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-bean.xml"); UserBean userBean = (UserBean) applicationContext.getBean("userBean"); System.out.println(userBean.getName()); } }
在这里直接使用ClassPathXmlApplicationContext进行xml解析,在这里xml解析的代码和GetBean的代码就不过多的描述了,ApplicationContext是BeanFactory的扩展,所以想要看这两部分源码的请看作者的上两篇博客Sprin源码解析;
- 接下来我们看一下ClassPathXmlApplicationContext的源码:
-
/** * Create a new ClassPathXmlApplicationContext with the given parent, * loading the definitions from the given XML files. * @param configLocations array of resource locations * @param refresh whether to automatically refresh the context, * loading all bean definitions and creating all singletons. * Alternatively, call refresh manually after further configuring the context. * @param parent the parent context * @throws BeansException if context creation failed * @see #refresh() */ public ClassPathXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { super(parent); // 支持解析多文件 setConfigLocations(configLocations); if (refresh) { refresh(); } }
在setConfigLocations方法中将资源文件放入configLocations全局变量中,,并且支持多文件解析,接下来我们你看一下重点,refresh方法;
- 源码refresh方法:
-
@Override public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. // 准备刷新上下文 prepareRefresh(); // Tell the subclass to refresh the internal bean factory. // 对beanFactory的各种功能填充,加载beanFactory,经过这个方法 applicationContext就有了BeanFactory的所有功能 ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. // 对beanFactory进行各种功能填充 prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. // 允许在context子类中对BeanFactory进行post-processing。 // 允许在上下文子类中对Bean工厂进行后处理 // 可以在这里进行 硬编码形式的 BeanFactoryPostProcessor 调用 addBeanFactoryPostProcessor postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. // 激活各种BeanFactory处理器 BeanFactoryPostProcessors是在实例化之前执行 invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. // 注册 拦截Bean创建 的Bean处理器,这里只是注册,真正地调用在getBean的时候 BeanPostProcessors实在init方法前后执行 doCreateBean方法中的 实例化方法中执行 // BeanPostProcessor执行位置:doCreateBean --> initializeBean --> applyBeanPostProcessorsBeforeInitialization 和 applyBeanPostProcessorsAfterInitialization registerBeanPostProcessors(beanFactory); // Initialize message source for this context. //为上下文初始化Message源,(比如国际化处理) 这里没有过多深入 initMessageSource(); // Initialize event multicaster for this context. //初始化应用消息广播,并放入 applicationEventMulticaster bean中 initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. //留给子类来初始化其它的bean onRefresh(); // Check for listener beans and register them. //在所有注册的bean中查找Listener bean,注册到消息广播器中 registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. //初始化剩下的单实例 finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. //完成刷新过程,通知生命周期护处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人(LifecycleProcessor 用来与所有声明的bean的周期做状态更新) finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
对于ApplicationContext来说,refresh方法几乎涵盖了所有的基础和扩展功能,接下来看一下这个方法都做了什么;
- 刷新上下文,初始化前的准备工作;
- 加载beanFactory,经过这个方法 applicationContext就有了BeanFactory的所有功能
- 对beanFactory进行各种功能填充
- 允许在这里对BeanFactory的二次加工,例如:可以在这里进行硬编码方法的对BeanFactory进行BeanFactoryPostProcessor或BeanPostProcessor的操作;在这里简单说一下BeanFactoryPostProcessor是在bean实例化之前执行的,BeanPostProcessor是在初始化方法前后执行的,BeanFactoryPostProcessor操作的是BeanFactoryBeanPostProcessor操作的是Bean,其次这里还涉及了一个扩展BeanDefinitionRegistryPostProcessor它是继承了BeanFactoryPostProcessor,并且还有自己的定义方法 postProcessBeanDefinitionRegistry,这个方法可以操作BeanDefinitionRegistry,BeanDefinitionRegistry有个最主要的方法就是registerBeanDefinition,可以注册BeanDefinition,可以用这方法来处理一下不受spring管理的一下bean;
- 处理所有的BeanFactoryPostProcessor,也可以说是激活BeanFactory处理器,在这个方法里会先处理BeanDefinitionRegistryPostProcessor,在处理BeanFactoryPostProcessor,因为BeanDefinitionRegistryPostProcessor有自己的定义,所以先执行;
- 注册BeanPostProcessors ,这里只是注册,真正地调用在getBean的时候 BeanPostProcessors是在init方法前后执行 BeanPostProcessor执行位置:doCreateBean --> initializeBean --> applyBeanPostProcessorsBeforeInitialization 和 applyBeanPostProcessorsAfterInitialization方法中;
- 为上下文初始化Message源,(比如国际化处理) 这里没有过多深入;
- 初始化应用消息广播,初始化 applicationEventMulticaster ,判断使用自定义的还是默认的;
- 留给子类来初始化其它的bean;
- 在所有注册的bean中查找 ApplicationListener bean,注册到消息广播器中;
- 初始化剩下的单实例(非懒加载),这里会是涉及conversionService,LoadTimeWeaverAware,冻结BeanFactory,初始化Bean等操作;
- 完成刷新过程,包括 清除 下文级资源(例如扫描的元数据),通知生命周期处理器lifecycleProcessor并strat,同时publish Event发出ContextRefreshEvent通知别人;
- 先来看prepareRefresh方法:
-
/** * Prepare this context for refreshing, setting its startup date and * active flag as well as performing any initialization of property sources. */ protected void prepareRefresh() { // Switch to active. this.startupDate = System.currentTimeMillis(); // 标志,指示是否已关闭此上下文 this.closed.set(false); // 指示此上下文当前是否处于活动状态的标志 this.active.set(true); if (logger.isDebugEnabled()) { if (logger.isTraceEnabled()) { logger.trace("Refreshing " + this); } else { logger.debug("Refreshi