概述

我相信很多人都像我一样迷惑,spring到底从哪里开始看,大多数视频,资料等,讲述从 AbstractWebApplicationContext.refresh 方法开始看,或给出一个这样的例子,

ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

开始debug,但仔细想一想,我们的项目是放在 tomcat 中运行的,tomcat 是如何加载 spring 的?又是怎么调用 refresh 方法的?是什么标志 spring 已经被启动?这些问题我们接下来深入了解。

寻找入口

在 web.xml 中,我们要配置一个监听器:ContextLoaderListener。关于 tomcat 是如何加载 web.xml,并启动监听器的,大家自己百度下,没有研究过 tomcat 源码就不做叙述了,到时候抽时间会给大家补上。

这段注释说明了此处关联了 spring,意思是说:辅助监听启动和关闭 spring 的根(WebApplicationContext),简单地依托 ContextLoader 和 ContextCleanupListener。自从 spring3.1起,支持注入底层应用上下文,凭借ContextLoaderListener(WebApplicationContext) 构造函数。在Servlet3.0+环境中允许编程式配置。

提到的构造函数:

/*** Create a new {@code ContextLoaderListener} with the given application context. This* constructor is useful in Servlet 3.0+ environments where instance-based* registration of listeners is possible through the {@link javax.servlet.ServletContext#addListener}* API.* <p>The context may or may not yet be {@linkplain* org.springframework.context.ConfigurableApplicationContext#refresh() refreshed}. If it* (a) is an implementation of {@link ConfigurableWebApplicationContext} and* (b) has <strong>not</strong> already been refreshed (the recommended approach),* then the following will occur:* <ul>* <li>If the given context has not already been assigned an {@linkplain* org.springframework.context.ConfigurableApplicationContext#setId id}, one will be assigned to it</li>* <li>{@code ServletContext} and {@code ServletConfig} objects will be delegated to* the application context</li>* <li>{@link #customizeContext} will be called</li>* <li>Any {@link org.springframework.context.ApplicationContextInitializer ApplicationContextInitializer org.springframework.context.ApplicationContextInitializer ApplicationContextInitializers}* specified through the "contextInitializerClasses" init-param will be applied.</li>* <li>{@link org.springframework.context.ConfigurableApplicationContext#refresh refresh()} will be called</li>* </ul>* If the context has already been refreshed or does not implement* {@code ConfigurableWebApplicationContext}, none of the above will occur under the* assumption that the user has performed these actions (or not) per his or her* specific needs.* <p>See {@link org.springframework.web.WebApplicationInitializer} for usage examples.* <p>In any case, the given application context will be registered into the* ServletContext under the attribute name {@link* WebApplicationContext#ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE} and the Spring* application context will be closed when the {@link #contextDestroyed} lifecycle* method is invoked on this listener.* @param context the application context to manage* @see #contextInitialized(ServletContextEvent)* @see #contextDestroyed(ServletContextEvent)*/public ContextLoaderListener(WebApplicationContext context) {super(context);}

英文不是很好,你们用有道词典试着翻译下,我们在浏览的过程中看到有许多 servlet 相关的东西,如果不熟悉看下 servlet3.1规范。我们学习源码以官方文档为主!!!

进入父类(ContextLoader)构造方法:

public ContextLoader(WebApplicationContext context) {this.context = context;
}

到这里大家可能会疑惑,在哪里启动? 当 tomcat 加载监听器时,会进行初始化,找到初始化函数 ContextLoaderListener.contextinitialized 函数:

/**
* Initialize the root web application context.
*/
@Override
public void contextInitialized(ServletContextEvent event) {initWebApplicationContext(event.getServletContext());
}

进入父类的 ContextLoader.initWebApplicationContext 函数:

/*** Initialize Spring's web application context for the given servlet context,* using the application context provided at construction time, or creating a new one* according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and* "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params.* @param servletContext current servlet context* @return the new WebApplicationContext* @see #ContextLoader(WebApplicationContext)* @see #CONTEXT_CLASS_PARAM* @see #CONFIG_LOCATION_PARAM*/public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {throw new IllegalStateException("Cannot initialize context because there is already a root application context present - " +"check whether you have multiple ContextLoader* definitions in your web.xml!");}servletContext.log("Initializing Spring root WebApplicationContext");Log logger = LogFactory.getLog(ContextLoader.class);if (logger.isInfoEnabled()) {logger.info("Root WebApplicationContext: initialization started");}long startTime = System.currentTimeMillis();try {// Store context in local instance variable, to guarantee that// it is available on ServletContext shutdown.if (this.context == null) {this.context = createWebApplicationContext(servletContext);}if (this.context instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;if (!cwac.isActive()) {// The context has not yet been refreshed -> provide services such as// setting the parent context, setting the application context id, etcif (cwac.getParent() == null) {// The context instance was injected without an explicit parent ->// determine parent for root web application context, if any.ApplicationContext parent = loadParentContext(servletContext);cwac.setParent(parent);}configureAndRefreshWebApplicationContext(cwac, servletContext);}}servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, 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 elapsedTime = System.currentTimeMillis() - startTime;logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");}return this.context;}catch (RuntimeException | Error ex) {logger.error("Context initialization failed", ex);servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);throw ex;}}

其中有两个主要方法,createWebApplicationContext 和 configureAndRefreshWebApplicationContext,这里不细述这些逻辑,主要是找到 spring 的入口,等学 springmvc 时我们再回来细说。

/*** Instantiate the root WebApplicationContext for this loader, either the* default context class or a custom context class if specified.* <p>This implementation expects custom contexts to implement the* {@link ConfigurableWebApplicationContext} interface.* Can be overridden in subclasses.* <p>In addition, {@link #customizeContext} gets called prior to refreshing the* context, allowing subclasses to perform custom modifications to the context.* @param sc current servlet context* @return the root WebApplicationContext* @see ConfigurableWebApplicationContext*/
protected WebApplicationContext createWebApplicationContext(ServletContext sc) {Class<?> contextClass = determineContextClass(sc);if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {throw new ApplicationContextException("Custom context class [" + contextClass.getName() +"] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");}return (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
}

BeanUtils.instantiateClass 方法主要用到了反射,返回一个 contextClass 类型的类并强转成 ConfigurableWebApplicationContext 类型。

protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {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 informationString idParam = sc.getInitParameter(CONTEXT_ID_PARAM);if (idParam != null) {wac.setId(idParam);}else {// Generate default id...wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +ObjectUtils.getDisplayString(sc.getContextPath()));}}wac.setServletContext(sc);String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);if (configLocationParam != null) {wac.setConfigLocation(configLocationParam);}// The wac environment's #initPropertySources will be called in any case when the context// is refreshed; do it eagerly here to ensure servlet property sources are in place for// use in any post-processing or initialization that occurs below prior to #refreshConfigurableEnvironment env = wac.getEnvironment();if (env instanceof ConfigurableWebEnvironment) {((ConfigurableWebEnvironment) env).initPropertySources(sc, null);}customizeContext(sc, wac);wac.refresh();
}

wac.refresh 方法会跳入 AbstractApplicationContext.refrsh 中,我们算是找到了spring的入口。

spring源码:入口相关推荐

  1. spring源码分析之分析入口

    目录 1.引言 2.spring5架构 2.1    核心容器(Core Container) 2.2    AOP和设备支持 2.3    数据访问及集成 2.4    Web 2.5    Mes ...

  2. 如何将spring源码作为导入eclipse中,变成一个普通的项目(git、github)

    引子: 怎么查看spring-framework的源码?是不是用压缩软件解压jar包,然后用编辑软件看?高端一点的,是在eclipse上面,按住Ctrl键跳转着看?这里我给大家介绍更加高端一点的方法. ...

  3. Spring源码剖析——Bean的配置与启动

    IOC介绍   相信大多数人在学习Spring时 IOC 和 Bean 算得上是最常听到的两个名词,IOC在学习Spring当中出现频率如此之高必然有其原因.如果我们做一个比喻的话,把Bean说成Sp ...

  4. 我该如何学习spring源码以及解析bean定义的注册

    如何学习spring源码 前言 本文属于spring源码解析的系列文章之一,文章主要是介绍如何学习spring的源码,希望能够最大限度的帮助到有需要的人.文章总体难度不大,但比较繁重,学习时一定要耐住 ...

  5. Spring源码-applicationcontext.xml解析过程

    为什么80%的码农都做不了架构师?>>>    Spring源码-applicationcontext.xml解析过程 核心流程:Spring中对于applicationcontex ...

  6. 【Spring源码分析】Bean加载流程概览

    代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...

  7. 【spring源码分析】IOC容器初始化(二)

    前言:在[spring源码分析]IOC容器初始化(一)文末中已经提出loadBeanDefinitions(DefaultListableBeanFactory)的重要性,本文将以此为切入点继续分析. ...

  8. Spring源码解析:自定义标签的解析过程

    2019独角兽企业重金招聘Python工程师标准>>> spring version : 4.3.x Spring 中的标签分为默认标签和自定义标签两类,上一篇我们探究了默认标签的解 ...

  9. spring源码分析第一天------源码分析知识储备

    spring源码分析第一天------源码分析知识储备 Spring源码分析怎么学? 1.环境准备: 2.思路    看:是什么? 能干啥    想:为什么?     实践:怎么做?         ...

  10. beaninfo详解源码解析 java_【Spring源码分析】Bean加载流程概览

    代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...

最新文章

  1. 利用三层交换机实现VLAN间路由(VLANIF)
  2. poj2718 Smallest Difference
  3. 一分钟理清Mysql的锁类型——《深究Mysql锁》
  4. P4735-最大异或和【可持久化Trie】
  5. 白话C++系列(27) -- RTTI:运行时类型识别
  6. 哇塞,打开一个页面访问了这么多次数据库??
  7. CentrOS7静默安装oracle11g
  8. c++ 转bcd码_还不会看汽车自诊断系统的故障码?三分钟教你怎么解决
  9. (1)vmware安装ubuntu13.10之后无法进入桌面;(2)ubuntu13.10无法显示eclipse的下拉菜单...
  10. Code First Migrations更新数据库结构的具体步骤
  11. Android做的第一个小程序
  12. Java开发设计——UML类图
  13. Photoshop 操作显示滞后问题及解决方法
  14. jquery.serialize
  15. 新书 | Kevin P. Murphy《概率机器学习:进阶》PDF开放下载
  16. 在阿里云上创建带gpu的ecs实例
  17. Magento的基本架构解析
  18. 常用搜索算法—盲目搜索和启发式搜索
  19. 当你在追梦的路上抱怨生活太累快要放弃的时候,不妨看看我的这篇文章
  20. android 字体删除线,android TextView 设置和取消删除线的两种方法

热门文章

  1. 总结---JavaScript数组
  2. php php-fpm安装 nginx配置php
  3. JavaScript 的语法(网摘)
  4. NHibernate Mapping文件中如何指定类的字节数组属性
  5. 人对光波的三种特性_面试题:常用塑胶材料的特性及用途,你能列出几个?
  6. c#日期转换周几_C#根据日期计算星期几的实例代码
  7. 向量的点积与叉乘的几何解释
  8. withRouter有什么用?干嘛用?为啥要用它啊???一分钟理解!
  9. WINDOWS下SQL2016安装
  10. C++ string类成员函数