在上一篇文章原创:springMVC源码深度剖析-(初始化)中讲到了创建本地上下文createWebApplicationContext(rootContext)的具体实现,接下来主要看创建上下文时初始化springMVC容器的部分
protected WebApplicationContext createWebApplicationContext(ApplicationContext parent) {
Class<?> contextClass = getContextClass();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Servlet with name '" + getServletName() +
"' will try to create custom WebApplicationContext context of class '" +
contextClass.getName() + "'" + ", using parent context [" + parent + "]");
}
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" + getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
//通过反射创建mvc容器
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
//设置环境
wac.setEnvironment(getEnvironment());
//设置根上下文为父上下文
wac.setParent(parent);
//设置springmvc.xml的路径
wac.setConfigLocation(getContextConfigLocation());
//初始化springmvc容器以及各种的相关配置,各种注入的Controller,配置文件等
configureAndRefreshWebApplicationContext(wac);
return wac;
}
1.configureAndRefreshWebApplicationContext(wac)
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
// 设置id.
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
}
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
//设置springmvc配置文件的名称
wac.setNamespace(getNamespace());
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
//默认实现是空的
postProcessWebApplicationContext(wac);
applyInitializers(wac);
//初始化springmvc容器以及各种的相关配置,各种注入的Controller,配置文件等
wac.refresh();
}
其中最重要的方法就是wac.refresh();,接下来我们将详细进行解析
2.wac.refresh()
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 准备对上下文进行刷新
prepareRefresh();
// 初始化beanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 为BeanFactory配置容器特性,例如类加载器、事件处理器等
prepareBeanFactory(beanFactory);
try {
// 为容器的某些子类指定特殊的BeanPost事件处理器
postProcessBeanFactory(beanFactory);
// 调用beanFactory处理器
invokeBeanFactoryPostProcessors(beanFactory);
// 注册beanFactory处理器.
registerBeanPostProcessors(beanFactory);
// 初始化消息源
initMessageSource();
// 初始化上下文事件广播.
initApplicationEventMulticaster();
// 初始化其它特殊bean,由子类来实现
onRefresh();
// 注册事件监听器.
registerListeners();
// 初始化所有单实例的bean,使用懒加载除外
finishBeanFactoryInitialization(beanFactory);
// 完成刷新并发布容器刷新事件
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();
}
}
}
这个方法做了很多事情,我们接下来只对重要的功能进行解析
2.1 obtainFreshBeanFactory
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//创建BeanFactory(先销毁所有的bean,然后在创建一个BeanFactory,最后在赋值)
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
创建BeanFactory会先销毁所有的bean,然后在创建一个BeanFactory,接下来我们看一下它是怎么执行的
@Override
protected final void refreshBeanFactory() throws BeansException {
//如果BeanFactory存在,就会进行销毁和关闭
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//创建BeanFactory
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);
}
}
createBeanFactory: 在创建BeanFactory的同时会判断是否有父BeanFactory,如果有设置,如果没有就不设置。
protected BeanFactory getInternalParentBeanFactory() {
//在创建BeanFactory的同时会判断是否有父BeanFactory,如果有设置,如果没有就不设置。
return (getParent() instanceof ConfigurableApplicationContext) ?
((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent();
}
loadBeanDefinitions(beanFactory):负责加载配置文件等信息,我们来看一下源代码
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 设置坏境
beanDefinitionReader.setEnvironment(this.getEnvironment());
//设置资源加载器
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
initBeanDefinitionReader(beanDefinitionReader);
//加载配置文件
loadBeanDefinitions(beanDefinitionReader);
}
loadBeanDefinitions(beanDefinitionReader):由AbstractRefreshableWebApplicationContext父类AbstractRefreshableConfigApplicationContext实现getConfigLocations方法,获取了web.xml配置的param-value的值,然后保存在数组里面
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws IOException {
//获取web.xml中<param-value>的值
String[] configLocations = getConfigLocations();
if (configLocations != null) {
for (String configLocation : configLocations) {
//读取配置文件
reader.loadBeanDefinitions(configLocation);
}
}
}
loadBeanDefinitions(configLocation):现在获取了param-value的值,接下去就开始读取配置文件,源代码如下
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) {
try {
//加载web.xml配置的param-value对应的资源
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
//对加载进来的资源进行解析并注册,也就是把用户定义的数据结构转化为 Ioc 容器中的特定数据结构
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;
}
}
总结:
(1)SpringMVC先创建BeanFactory时先销毁所有Bean,关闭BeanFactory,然后重新创建一个BeanFactory,并将其赋给BeanFactory实例变量。
(2)创建好了BeanFactory,并设置好环境,并然后通过ResourcePatternResolver加载web.xml配置的param-value对应的资源,loadBeanDefinitions进行对加载进来的资源进行解析,在解析之前先进行转码,是通过EncodedResource对资源文件进行转码
(3)对读取进来的配置文件进行验证,是否是XML格式的,然后配置文件中的Bean的id、name、class等属性的解析并设置到BeanDefinition然后放到放到BeanDefinitionHolder中,并放入到IOC容器中建立对应的数据结构。是以Document形式的存储的。