这篇来看下AbstractApplicationContext中的refresh()方法,这个方法初始化且启动整个spring容器的核心。方法名字理解起来就是刷新的意思,意味着刷新整个Spring容器,做好一切准备。
public void refresh() throws BeansException, IllegalStateException {
//初始化容器需要加锁,防止并发加载,startupShutdownMonitor锁对象
synchronized (this.startupShutdownMonitor) {
// 预加载刷新,里面包含初始化准备工作
prepareRefresh();
// 创建bean工厂,加载初始化beanDefinitionMap、依赖关系、注入属性等等对象,注入对象解析的实现在里面
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 准备beanFactory绑定到context容器
prepareBeanFactory(beanFactory);
try {
// 允许在context子类中对bean工厂进行后处理
postProcessBeanFactory(beanFactory);
// 实例化并调用所有已注册的beanfactorypostprocessor bean对象
invokeBeanFactoryPostProcessors(beanFactory);
// 注册拦截bean创建的bean处理器.
registerBeanPostProcessors(beanFactory);
// 初始化国际化消息资源
initMessageSource();
// 初始化应用事件广播器,用来触发事件监听
initApplicationEventMulticaster();
// 初始化context子类的特殊bean对象
onRefresh();
// 注册监听器和事件类型,就是把所有对应的监听器和事件类型保存到应用容器里
registerListeners();
// 实例化所有剩下的(非惰性初始化)单例.
finishBeanFactoryInitialization(beanFactory);
// 最后一步:发布刷新完毕相应的事件ContextRefreshedEvent
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// 出现刷新异常,则销毁已经创建的单例以避免占用资源。
destroyBeans();
// 重置激活标志
cancelRefresh(ex);
// 抛出异常
throw ex;
}
finally {
// 清楚部分加载解析用到的beanmeta缓存,因为我们已经在spring核心容器里重置了常见的内部缓存 可能不再需要单身bean的元数据了
resetCommonCaches();
}
}
}
咋一看这个方法的代码很少,其实里面调用很多层框架所需要的实现,(初略总结)大概做了如下事情:
加载PropertySources、环境参数、初始化并构建BeanFactory、解析注册XmlBeanDefinition,初始化创建装载bean、解析Bean之间的依赖关系、国际化消息初始化、初始化监听事件广播、注册所有监听器、事件发布通知结束等。
预加载方法:
protected void prepareRefresh() {
this.startupDate = System.currentTimeMillis();//记录启动时间
this.closed.set(false);//容器没有关闭标识
this.active.set(true);//容器启动激活标识
if (logger.isInfoEnabled()) {
logger.info("Refreshing " + this);
}
// 初始化加载系统上下文环境属性参数资源,在ContextLoader可能已加载
initPropertySources();
// 校验所有的设置Required的Properties,在ConfigurablePropertyResolver#setRequiredProperties设置
getEnvironment().validateRequiredProperties();
// 初始化容器早期事件Set集合
this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}
本章重点分析BeanFactory初始化过程:
- 预准备 初始化环境property sources
- 初始化BeanFactory,解析xml注册配置和bean对象
- 创建并刷新BeanFactory,解析注册Bean
AbstractRefreshableApplicationContext#refreshBeanFactory方法实现
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();//创建BeanFactory对象
beanFactory.setSerializationId(getId());//序列化id
customizeBeanFactory(beanFactory);//
loadBeanDefinitions(beanFactory);//加载资源解析BeanDefinition
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
可以看到refreshBeanFactory中的方法loadBeanDefinitions加载资源并去解析xml,然后创建BeanDefinition等操作
XmlWebApplicationContext的具体实现loadBeanDefinitions方法,第一个参数location就是web.xml配置的configLocations路径,第二参数actualResources的资源Set
public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader == null) {
throw new BeanDefinitionStoreException(
"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
}
if (resourceLoader instanceof ResourcePatternResolver) {
// Resource pattern matching available.
try {
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
int loadCount = loadBeanDefinitions(resources);
if (actualResources != null) {
for (Resource resource : resources) {
actualResources.add(resource);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
}
return loadCount;
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Could not resolve bean definition resource pattern [" + location + "]", ex);
}
}
else {
// Can only load single resources by absolute URL.
Resource resource = resourceLoader.getResource(location);
int loadCount = loadBeanDefinitions(resource);
if (actualResources != null) {
actualResources.add(resource);
}
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
}
return loadCount;
}
}