spring源码:入口
概述
我相信很多人都像我一样迷惑,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源码:入口相关推荐
- spring源码分析之分析入口
目录 1.引言 2.spring5架构 2.1 核心容器(Core Container) 2.2 AOP和设备支持 2.3 数据访问及集成 2.4 Web 2.5 Mes ...
- 如何将spring源码作为导入eclipse中,变成一个普通的项目(git、github)
引子: 怎么查看spring-framework的源码?是不是用压缩软件解压jar包,然后用编辑软件看?高端一点的,是在eclipse上面,按住Ctrl键跳转着看?这里我给大家介绍更加高端一点的方法. ...
- Spring源码剖析——Bean的配置与启动
IOC介绍 相信大多数人在学习Spring时 IOC 和 Bean 算得上是最常听到的两个名词,IOC在学习Spring当中出现频率如此之高必然有其原因.如果我们做一个比喻的话,把Bean说成Sp ...
- 我该如何学习spring源码以及解析bean定义的注册
如何学习spring源码 前言 本文属于spring源码解析的系列文章之一,文章主要是介绍如何学习spring的源码,希望能够最大限度的帮助到有需要的人.文章总体难度不大,但比较繁重,学习时一定要耐住 ...
- Spring源码-applicationcontext.xml解析过程
为什么80%的码农都做不了架构师?>>> Spring源码-applicationcontext.xml解析过程 核心流程:Spring中对于applicationcontex ...
- 【Spring源码分析】Bean加载流程概览
代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...
- 【spring源码分析】IOC容器初始化(二)
前言:在[spring源码分析]IOC容器初始化(一)文末中已经提出loadBeanDefinitions(DefaultListableBeanFactory)的重要性,本文将以此为切入点继续分析. ...
- Spring源码解析:自定义标签的解析过程
2019独角兽企业重金招聘Python工程师标准>>> spring version : 4.3.x Spring 中的标签分为默认标签和自定义标签两类,上一篇我们探究了默认标签的解 ...
- spring源码分析第一天------源码分析知识储备
spring源码分析第一天------源码分析知识储备 Spring源码分析怎么学? 1.环境准备: 2.思路 看:是什么? 能干啥 想:为什么? 实践:怎么做? ...
- beaninfo详解源码解析 java_【Spring源码分析】Bean加载流程概览
代码入口 之前写文章都会啰啰嗦嗦一大堆再开始,进入[Spring源码分析]这个板块就直接切入正题了. 很多朋友可能想看Spring源码,但是不知道应当如何入手去看,这个可以理解:Java开发者通常从事 ...
最新文章
- 利用三层交换机实现VLAN间路由(VLANIF)
- poj2718 Smallest Difference
- 一分钟理清Mysql的锁类型——《深究Mysql锁》
- P4735-最大异或和【可持久化Trie】
- 白话C++系列(27) -- RTTI:运行时类型识别
- 哇塞,打开一个页面访问了这么多次数据库??
- CentrOS7静默安装oracle11g
- c++ 转bcd码_还不会看汽车自诊断系统的故障码?三分钟教你怎么解决
- (1)vmware安装ubuntu13.10之后无法进入桌面;(2)ubuntu13.10无法显示eclipse的下拉菜单...
- Code First Migrations更新数据库结构的具体步骤
- Android做的第一个小程序
- Java开发设计——UML类图
- Photoshop 操作显示滞后问题及解决方法
- jquery.serialize
- 新书 | Kevin P. Murphy《概率机器学习:进阶》PDF开放下载
- 在阿里云上创建带gpu的ecs实例
- Magento的基本架构解析
- 常用搜索算法—盲目搜索和启发式搜索
- 当你在追梦的路上抱怨生活太累快要放弃的时候,不妨看看我的这篇文章
- android 字体删除线,android TextView 设置和取消删除线的两种方法
热门文章
- 总结---JavaScript数组
- php php-fpm安装 nginx配置php
- JavaScript 的语法(网摘)
- NHibernate Mapping文件中如何指定类的字节数组属性
- 人对光波的三种特性_面试题:常用塑胶材料的特性及用途,你能列出几个?
- c#日期转换周几_C#根据日期计算星期几的实例代码
- 向量的点积与叉乘的几何解释
- withRouter有什么用?干嘛用?为啥要用它啊???一分钟理解!
- WINDOWS下SQL2016安装
- C++ string类成员函数