在web项目中使用spring的时候,咱们会在web.xml中加入以下配置:html
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
这个配置增长了一个listener,这个ContextLoaderListener 实现了ServletContextListener 。咱们在平常工做中也会定义一些listener。用于在应用启动的时候作些什么。由于咱们知道servelt容器在启动的时候,Listener 类中的contextInitialized()方法将会被调用。spring中ContextLoaderListener也把这个做为起始点来初始化,contextInitialized()方法的实现以下:java
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类中的,代码以下,能够先不要读这个代码,大概扫一眼,而后继续根据后面的文字描述跟踪逻辑:web
//为了方便初步的阅读,我删除了一些占用篇幅的地方 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的内容以下:spring
org.springframework.web.context.WebApplicationContext=org.springframework.web.context.support.XmlWebApplicationContext
咱们继续看initWebApplicationContext方法。这个方法很长,可是实际上它主要作了两件事情:app
1.建立一个ConfigurableWebApplicationContext实例。ide
2.根据建立的ConfigurableWebApplicationContext实例,来配置并刷新WebApplicationContext。源码分析
先看第一步,建立ConfigurableWebApplicationContext,方法是createWebApplicationContext()。代码以下:post
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反射来建立这个类的实例并返回。this
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接口是如何找到实现类的-源码分析