Spring 源码分析-1-启动
Spring 源码分析-1-启动
在web项目中使用spring的时候,我们会在web.xml中加入如下配置:
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
这个配置增加了一个listener,这个ContextLoaderListener 实现了ServletContextListener 。我们在日常工作中也会定义一些listener。用于在应用启动的时候做些什么。因为我们知道servelt容器在启动的时候,Listener 类中的contextInitialized()方法将会被调用。spring中ContextLoaderListener也把这个作为起始点来初始化,contextInitialized()方法的实现如下:
public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
  	private ContextLoader contextLoader;
	public void contextInitialized(ServletContextEvent event) {
		this.contextLoader = createContextLoader();//此方法直接return null了
		if (this.contextLoader == null) {
			this.contextLoader = this;
		}
		this.contextLoader.initWebApplicationContext(event.getServletContext());
	}
	@Deprecated
	protected ContextLoader createContextLoader() {
		return null;
	}
}
根据上面的代码,在调用contextInitialized方法里边的代码很少,先是给contextLoader赋值了,然后调用了initWebApplicationContext方法。这个方法就是我们窥探的入口,它是在ContextLoader类中的,代码如下,可以先不要读这个代码,大概扫一眼,然后继续根据后面的文字描述跟踪逻辑:
//为了方便初步的阅读,我删除了一些占用篇幅的地方
public class ContextLoader {
  public static final String CONTEXT_CLASS_PARAM = "contextClass";
  public static final String CONTEXT_ID_PARAM = "contextId";
  public static final String CONTEXT_INITIALIZER_CLASSES_PARAM = "contextInitializerClasses";
  public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";//web.xml里有,熟悉吧
  public static final String LOCATOR_FACTORY_SELECTOR_PARAM = "locatorFactorySelector";
  public static final String LOCATOR_FACTORY_KEY_PARAM = "parentContextKey";
  private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties";
  private static final Properties defaultStrategies;
  	static {
      //这个静态代码块读取了ContextLoader.properties这个配置文件。在spring源码中可以找到这个配置文件
      //内容只有一行 指定WebApplicationContext的实现类为XmlWebApplicationContext
		try {
			ClassPathResource resource = new ClassPathResource("ContextLoader.properties",                                                                                 ContextLoader.class);
			defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
		}
		catch (IOException ex) {
			throw new IllegalStateException("Could not load 'ContextLoader.properties': "
                                            + ex.getMessage());
		}
	}
  	private static volatile WebApplicationContext currentContext;
  	private WebApplicationContext context;
  	private BeanFactoryReference parentContextRef;
	public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
		Log logger = LogFactory.getLog(ContextLoader.class);
		servletContext.log("Initializing Spring root WebApplicationContext");
		if (logger.isInfoEnabled()) {
			logger.info("Root WebApplicationContext: initialization started");
		}
		long startTime = System.currentTimeMillis();
		try {
			if (this.context == null) {
              // 这个create方法创建了一个ConfigurableWebApplicationContext实例
				this.context = createWebApplicationContext(servletContext);
			}
			if (this.context instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac =
                             (ConfigurableWebApplicationContext) this.context;
				if (!cwac.isActive()) {//isActive默认为false,所以会进入if,执行下面的代码
					if (cwac.getParent() == null) {
						ApplicationContext parent = loadParentContext(servletContext);
						cwac.setParent(parent);
					}
                  //
					configureAndRefreshWebApplicationContext(cwac, servletContext);
				}
			}
			servletContext.setAttribute("一个变量名", this.context);
			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
			if (ccl == ContextLoader.class.getClassLoader()) {
				currentContext = this.context;
			}
			else if (ccl != null) {
				currentContextPerThread.put(ccl, this.context);
			}
			if (logger.isInfoEnabled()) {
				long t = System.currentTimeMillis() - startTime;
				logger.info("Root WebApplicationContext: initialization completed in"+t+" ms");
			}
			return this.context;
		}
		catch (RuntimeException ex) {
			logger.error("Context initialization failed", ex);
			servletContext.setAttribute("一个很长变量名", ex);
			throw ex;
		}
		catch (Error err) {
			logger.error("Context initialization failed", err);
			servletContext.setAttribute("一个很长变量名", err);
			throw err;
		}
	}
}
在上面的贴出的的代码中,我们可以看到这个ContextLoader类有一个静态代码块,静态代码块会在累加在的时候就执行了,这个代码块执行的内容很简单,就是找到一个名为“ContextLoader.properties”的配置文件,并将这个Properties赋给defaultStrategies变量。ContextLoader.properties的内容如下:
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
我们继续看initWebApplicationContext方法。这个方法很长,但是实际上它主要做了两件事情:
1.创建一个ConfigurableWebApplicationContext实例。
2.根据创建的ConfigurableWebApplicationContext实例,来配置并刷新WebApplicationContext。
先看第一步,创建ConfigurableWebApplicationContext,方法是createWebApplicationContext()。代码如下:
	protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
		Class<?> contextClass = determineContextClass(sc);
		if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
			throw new ApplicationContextException("日志描述");
		}
		return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
	}
这个方法先调用determineContextClass来找到一个类,然后return语句中通过BeanUtils反射来创建这个类的实例并返回。
determineContextClass类中,其实就是利用刚才读到的配置文件“ContextLoader.properties”,从这个文件中得到配置的类名,根据类名返回Class对象。代码简单就不贴了。
在看第二步,刷新WebApplicationContext。即调用了configureAndRefreshWebApplicationContext(...)方法。这个方法里边做的事情很重要。先看看代码:
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
		if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
            // 这里我删除了一段无关紧要的代码,起逻辑和下面的两句一样。
            String xxx= ...;
			wac.setId(xxx)
		}
		wac.setServletContext(sc);
       // 这里得到了我们在web.xml中配置的一个值,即:contextConfigLocation的值,也就是我们的spring文件
		String initParameter = sc.getInitParameter(CONFIG_LOCATION_PARAM);
		if (initParameter != null) {
          // 设置spring的配置文件
			wac.setConfigLocation(initParameter);
		}
		customizeContext(sc, wac);//后面分析
		wac.refresh();//后面分析
	}
方法中,最后两行的方法调用。先看customizeContext(sc,wac)方法,方法的里边通过serveltContext 的getInitParameter方法得到contextInitializerClasses。如果没配置,就什么也不做,进入之后,可以看到头两行的内容如下:
List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
				determineContextInitializerClasses(servletContext);
if (initializerClasses.size() == 0) {
	// no ApplicationContextInitializers have been declared -> nothing to do
    return;
}
第一行,通过determineContextInitializerClasser方法来获取配置的contextInitializerClasses变量值,这个值是一个class类名,多个的话用逗号隔开。如果没有配置的话,代码就会执行if size=0这一句,然后return了。
第二行wac.refresh()方法调用非常重要。这个方法实际上完成了spring 容器的初始化,代码如下:
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();
			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);
			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);
				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);
				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);
				// Initialize message source for this context.
				initMessageSource();
				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();
				// Initialize other special beans in specific context subclasses.
				onRefresh();
				// Check for listener beans and register them.
				registerListeners();
				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);
				// Last step: publish corresponding event.
				finishRefresh();
			}
			catch (BeansException ex) {
				destroyBeans();cancelRefresh(ex);
				throw ex;
			}
		}
	}
至此spring容器初始化完成了,这个wac.refresh()代码暂时不做深究,本篇主要讨论的是web.xml中的一行配置如何导致了spring的启动过程。本文代码是基于spring3.2.5RELASE。
SpringMVC是怎么工作的,SpringMVC的工作原理
spring 异常处理。结合spring源码分析400异常处理流程及解决方法
Netty系列
Mybatis Mapper接口是如何找到实现类的-源码分析
Spring 源码分析-1-启动的更多相关文章
- Spring源码分析专题 —— IOC容器启动过程(上篇)
		
声明 1.建议先阅读<Spring源码分析专题 -- 阅读指引> 2.强烈建议阅读过程中要参照调用过程图,每篇都有其对应的调用过程图 3.写文不易,转载请标明出处 前言 关于 IOC 容器 ...
 - 【Spring源码分析】非懒加载的单例Bean初始化前后的一些操作
		
前言 之前两篇文章[Spring源码分析]非懒加载的单例Bean初始化过程(上篇)和[Spring源码分析]非懒加载的单例Bean初始化过程(下篇)比较详细地分析了非懒加载的单例Bean的初始化过程, ...
 - 【Spring源码分析】配置文件读取流程
		
前言 Spring配置文件读取流程本来是和http://www.cnblogs.com/xrq730/p/6285358.html一文放在一起的,这两天在看Spring自定义标签的时候,感觉对Spri ...
 - Spring 源码分析之 bean 依赖注入原理(注入属性)
		
最近在研究Spring bean 生命周期相关知识点以及源码,所以打算写一篇 Spring bean生命周期相关的文章,但是整理过程中发现涉及的点太多而且又很复杂,很难在一篇文章中把Spri ...
 - spring源码分析系列 (2) spring拓展接口BeanPostProcessor
		
Spring更多分析--spring源码分析系列 主要分析内容: 一.BeanPostProcessor简述与demo示例 二.BeanPostProcessor源码分析:注册时机和触发点 (源码基于 ...
 - Spring源码分析:非懒加载的单例Bean初始化前后的一些操作
		
之前两篇文章Spring源码分析:非懒加载的单例Bean初始化过程(上)和Spring源码分析:非懒加载的单例Bean初始化过程(下)比较详细地分析了非懒加载的单例Bean的初始化过程,整个流程始于A ...
 - 框架-spring源码分析(一)
		
框架-spring源码分析(一) 参考: https://www.cnblogs.com/heavenyes/p/3933642.html http://www.cnblogs.com/BINGJJF ...
 - Spring源码分析专题——目录
		
Spring源码分析专题 -- 阅读指引 IOC容器 Spring源码分析专题 -- IOC容器启动过程(上篇) Spring源码分析专题 -- IOC容器启动过程(中篇) Spring源码分析专题 ...
 - Spring源码分析专题 —— 阅读指引
		
阅读源码的意义 更深入理解框架原理,印象更深刻 学习优秀的编程风格.编程技巧.设计思想 解决实际问题,如修复框架中的bug,或是参考框架源码,结合实际业务需求编写一个独有的框架 阅读源码的方法 首先是 ...
 
随机推荐
- Jekins相关笔记
			
Jekins忘记密码操作 https://blog.csdn.net/intelrain/article/details/78749635 Jekins重启 https://www.cnblogs.c ...
 - 16.kubernetes的RBAC
			
role 分为clsterrole和role 我们从普通的role 开始理解起 [root@master ~]# kubectl create role pod-read --verb=get,lis ...
 - 解决Docker中运行的MySQL中文乱码
			
docker exec -it mysql bash 如果没有安装vim,请参考 解决Docker容器中不能用vim编辑文件 vim /etc/mysql/mysql.conf.d/mysql.cnf ...
 - shell脚本自动化部署服务
			
shell脚本自动化部署 !/bin/bash #export PATH=$PATH:/export/maven/bin run_flag_dir="/data0/shell/deploy_ ...
 - 倍增\ tarjan求lca
			
对于每个节点v,记录anc[v][k],表示从它向上走2k步后到达的节点(如果越过了根节点,那么anc[v][k]就是根节点). dfs函数对树进行的dfs,先求出anc[v][0],再利用anc[v ...
 - Magento 架构基础知识概述
			
Megento 架构基础知识概述 Magento整合了面向对象的基于PHP的应用程序的核心架构原则.这些一般原则的综合讨论既有在线的,也有印刷形式.以下讨论主要关注这些主题如何直接应用于Magento ...
 - python之路day06--python2/3小区别,小数据池的概念,编码的进阶str转为bytes类型,编码和解码
			
python2#print() print'abc'#range() xrange()生成器#raw_input() python3# print('abc')# range()# input() = ...
 - c#操作IIS之IISHelper
			
//----------------------------------------------------------------------- // <copyright file=&quo ...
 - 如何将JPG格式的图片转换成PNG格式
			
study from : https://jingyan.baidu.com/article/6079ad0e63a4fc28ff86db37.html
 - Vue(小案例_vue+axios仿手机app)_图文列表实现
			
一.前言 1.导航滑动实现 2.渲染列表信息时让一开始加载的时候就请求数据 3.根据路由的改变,加载图文的改变(实现用户访问网站时可能执行的三个操作) 二.主要内容 1.导航滑动实现: (1)演示 ...