带着疑问开始

web.xml的顺序问题

先拿一个最简单的spring mvc web.xml来说问题,如下图:如果我将三者的顺序倒置或是乱置,会产生什么结果呢?

启动报错?还是加载未知结果?还是毫无影响?

结果是什么呢?让我们用实践来证明一下:go->jetty-spring-context project 现场演示

//todo 之后贴出结果

applicationContext.xml和spring-mvc-servlet.xml怎么配置

最简单的配置(这样不仅产生两个容器而且每个容器都生成一样的bean)

applicationContext:

spring-mvc-servlet.xml

正确的配置其中之一

 applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<!--正确的配置-->
<context:component-scan base-package="com.meituan.jetty.spring">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans> spring-mvc-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--正确的配置-->
<context:component-scan base-package="com.meituan.jetty.spring.controller"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>

可以么?go->jetty-spring-context project 现场演示问题来了,通过结果我们发现,确实有两个容器,那么由于两个容器同时可以有一样的beans,那是否可以直接去掉ApplicationContext容器呢?

applicationContext.xml和spring-mvc-servlet.xml的初始化顺序如何

结果:applicationContext.xml的初始化优先于spring-mvc-servlet.xml

两个context容器到底是怎样的

  1. 两个容器分别是怎么初始化的呢?
  2. 为什么applicationContext容器就是mvc context容器的父容器呢?
  3. 这两个容器分别是什么类型的applicationContext实现呢?(我们知道applicationContext是接口)

下边的图是applicationContext接口的少部分实现

深入源码解决疑难

为什么不管怎么配置app.xml总是在mvc之前初始化

下边让我们用源码一步一步来分析其中的奥妙

启动jetty容器入口

......................前边还有一大段代码

初始化调用的是mms server的start方法,其实server没有start方法,是它的父类AbstractLifeCyle的start方法,然后再回调,我们来看下server的结构

在继续讲server是怎么一步步调用之前,我们需要知道两个事情

  1. ContextLoaderListener

  2. DispatcherServlet

ContextLoaderListener和DispatcherServlet的实质(插播)

contextLoaderListerner实质是实现了EventListener的一个事件监听器

相关事件通知,在我的另外一篇wiki中有详细介绍:

事件通知机制深入源码#事件通知机制深入源码-ApplicationListener

或者 直接看这里:Spring事件通知机制详解

contextLoaderListerner 的方法  contextInitialized  会被回调;
DispatcherServlet实质上是一个servlet,当然,这个不用说也看的很清楚

DispatcherServlet 的父类FrameworkServlet的方法initServletBean会被回调

   这时候我们还要知道一个事情:contextLoaderListerner 和 DispatcherServlet 是在spring的两个package里边,前者在spring-web里边,后者在spring-webmvc里边,这个对后边的理解有帮助

启动jetty容器入口(续)

为什么listener能够无论以哪种姿态都会优先于servlet执行呢?

要解决这个问题,我们先看下listener是在何时被回调的:

首先大概浏览下这个图,这里对WebAppContext和ContextHandler大概有一个映像(当然,这个是jetty的源码)

jetty启动,会初始化一个WebAppContext(WebAppContext 继承了 ServletContextHandler ,ServletContextHandler  继承了ContextHandler ,而且他们都实现了 )对象;

最终,WebAppContext对象的startContext()方法会被实现,如下图调用链:

而startContext方法又做了什么事情呢?带着疑问,我们走进下边的代码:

我们发现,ContextHandler里边存了一个listener的集合,而恰巧我们的 ContextLoaderListener 实例也在这个集合当中;

我们看到这里把ContextLoaderListener和event事件传递给了callContextInitialized方法,所以ContextLoaderListener的contextInitialized方法最终会被调用,

到此为止,我们就解释了ContextLoaderListener是会被合理的初始化的;

至于ContextLoaderListener初始化的详细过程,请看这里:浅谈jetty如何初始化spring容器-ContextLoaderListener初始化context容器的过程

咦?好像有什么不对的地方。哦,对,本来是WebAppContext的startContext方法,怎么会跑到ContextHandler的startContext方法,看上图,

是子类父类的关系,原来如此;

看调用链,再来说说 boot 本来调用server的start,为什么会走到lifeCycle呢?

原来 server 继承了 AbstractLifeCycle,jetty源代码里边大量运用了 模板方法和类模板方法,我们开发的时候也可以学习这种设计模式,减少重复代码,提高代码复用率。

讲了这么多,还没讲到 为什么 listener 总是在 servlet之前执行呢?

莫急,且听下边讲解

如下,WebAppContext的 doStart 方法被调用,此时WebAppContext自己实现了一部分,其余直接调用父类->ContextHandler的doStart方法

(咦,不对,父类不是ServletContextHandler么;哦,ServletContextHandler并未重写这个方法)

接下来调用ContextHandler的doStart方法

ContextHandler再次调用子类WebAppContext的方法 startContext()

WebAppContext 首先调用 startWebApp,然后 startWebApp 再次调用父类 ServletContextHandler的 startContext方法

这里就比较有意思了:ServletContextHandler首先调用父类,也就是ContextHandler的startContext方法,还记得父类的这个方法发生了什么吗?

对!父类这个方法里边初始化了 ContextLoadListener ,也就是初始化了所有的 事件通知 !!!

事件通知完成之后,开始调用servlet的initialize方法,初始化servlet;servlet初始化详解:深入浅出jetty初始化spring容器-DispatcherServlet初始化context容器的过程

  也就是说:frameworkServlet初始化方法的回调是由ServletContextHandler的startContext方法引起的!!!

看下边 listener和servlet的执行顺序:

至此为止,我们剖析了 jetty初始化 为什么 listener的执行一定会先于servlet!!!

ContextLoaderListener初始化context容器的过程

ContextLoaderListener结构

ContextLoaderListener入口

首先,回调ContextLoaderListener的contextInitialized方法

然后调用父类ContextLoader的contextInitialized方法,第一次初始化的时候 org.springframework.web.context.WebApplicationContext.ROOT == null

紧接着在1的时候创建context

我们看看 context 到底是怎么创建的?创建的是哪种类型?红框决定了创建哪种类型的 applicationContext

如下图,通过strategy决定创建哪种类型

strategy又是怎么初始化的呢?

look

可见,是这个配置文件决定了,wepApplicationContext的类型是XmlWebApplicationContext,接下来是configAndRefresh

最终调用 AbstractApplicationContext的refresh方法,根据配置文件内容,开始初始化;

AbstractApplicationContext的refresh的初始化都知道吧?不知道的话,可以看我这篇关于spring初始化顺序的文章:Spring Init&Destroy#spring容器的主要入口

DispatcherServlet初始化context容器的过程

DispatcherServlet结构

DispatcherServlet入口

初始化webApplicationContext,

创建webApplicationContext

这里的contextClass是这么决定的

最终也是调用refresh实例化的

最终完成第二个容器的初始化

jetty加载spring-context容器源码分析的更多相关文章

  1. Spring IOC 容器源码分析 - 获取单例 bean

    1. 简介 为了写 Spring IOC 容器源码分析系列的文章,我特地写了一篇 Spring IOC 容器的导读文章.在导读一文中,我介绍了 Spring 的一些特性以及阅读 Spring 源码的一 ...

  2. Spring IOC 容器源码分析系列文章导读

    1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本.经过十几年的迭代,现在的 Spring 框架已经非常成熟了.Spring ...

  3. Spring IOC 容器源码分析 - 余下的初始化工作

    1. 简介 本篇文章是"Spring IOC 容器源码分析"系列文章的最后一篇文章,本篇文章所分析的对象是 initializeBean 方法,该方法用于对已完成属性填充的 bea ...

  4. Spring IOC 容器源码分析 - 填充属性到 bean 原始对象

    1. 简介 本篇文章,我们来一起了解一下 Spring 是如何将配置文件中的属性值填充到 bean 对象中的.我在前面几篇文章中介绍过 Spring 创建 bean 的流程,即 Spring 先通过反 ...

  5. Spring IOC 容器源码分析 - 循环依赖的解决办法

    1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的.在本篇文章中,我会首先向大家介绍一下什么是循环依赖.然后,进入源码分析阶段.为了更好的说明 Spring 解决循环依赖的办法,我 ...

  6. Spring IOC 容器源码分析 - 创建原始 bean 对象

    1. 简介 本篇文章是上一篇文章(创建单例 bean 的过程)的延续.在上一篇文章中,我们从战略层面上领略了doCreateBean方法的全过程.本篇文章,我们就从战术的层面上,详细分析doCreat ...

  7. Spring IOC 容器源码分析 - 创建单例 bean 的过程

    1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑.对于已实例化好的单例 bean,getBean(String) 方法并不会再一次去 ...

  8. 最简 Spring IOC 容器源码分析

    前言 BeanDefinition BeanFactory 简介 Web 容器启动过程 bean 的加载 FactoryBean 循环依赖 bean 生命周期 公众号 前言 许多文章都是分析的 xml ...

  9. Spring Boot JDBC:加载DataSource过程的源码分析及yml中DataSource的配置

    装载至:https://www.cnblogs.com/storml/p/8611388.html Spring Boot实现了自动加载DataSource及相关配置.当然,使用时加上@EnableA ...

  10. Spring IOC 容器源码分析

    声明!非原创,本文出处 Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 S ...

随机推荐

  1. js/jquery 实时监听输入框值变化的完美方案:oninput & onpropertychange

    (1)     先说jquery, 使用 jQuery 库的话,只需要同时绑定 oninput 和 onpropertychange 两个事件就可以了,示例代码: $('#username').bin ...

  2. T-SQL 公用表表达式(CTE)

    公用表表达式(CTE) 在编写T-SQL代码时,往往需要临时存储某些结果集.前面我们已经广泛使用和介绍了两种临时存储结果集的方法:临时表和表变量.除此之外,还可以使用公用表表达式的方法.公用表表达式( ...

  3. FastSocket.Net

    Overview FastSocket是一个轻量级易扩展的c#异步socket通信库,项目开始于2011年,经过近3年不断调整与改进,目前在功能和性能上均有不错的表现. 项目地址:https://gi ...

  4. NFC 与 Windows Phone 的那点事儿

    说起NFC这个词儿应该已经不陌生了,在我们的生活中有很多使用场景都是使用的这项技术,例如公交卡,门禁,还有银联的闪付卡等等.并且近些年在移动设备上使用的场景也越来越多,例如 对 NFC TAG 的读写 ...

  5. mysql同主机数据库复制

    我们有时候需要在测试环境和生产环境下做交换.以下可以快速的复制数据库,支持大容量(1G以上) 1.将database_DEV数据库导成database_DEV.contents.gz压缩文件 mysq ...

  6. 关于同一台机器上安装多个sql实例的连接方法

    由于客户需要在一台服务器上安装了两个sql服务器(一个sql2000,一个是sql2005,其实例名不同),默认的端口1433被先安装的sql2000使用,后来安装的的随机启用了一个3045端口.其中 ...

  7. 常用CSS Reset汇总

    什么是Css Reset呢? 在 HTML标签在浏览器里有默认的样式,不同浏览器的默认样式之间也会有差别.在切换页面的时候,浏览器的默认样式往往会给我们带来麻烦,影响开发效率.所以解决的方法就是一开始 ...

  8. ef 更新导航属性

    总之,要让所有的东西,都被DbContext管理状态 1.查出来,改了,再提交 2.new 出来,attach,再改,再提交 以上两种都较好理解 3.new出来,改了,再attach,在改状态,再提交 ...

  9. vuejs入门小demo-搜索大全

    这个demo非常适合入门的同学,不再是简单的todolist.用到的知识点有组件通信,过渡效果,vue-rsource,还有一些基本的vue指令. 先放一张截图: 是不是感觉高端大气上档次呢,演示地址 ...

  10. [团队项目]SCRUM项目4.0

    1.准备看板. 形式参考图4. 2.任务认领,并把认领人标注在看板上的任务标签上. 先由个人主动领任务,PM根据具体情况进行任务的平衡. 然后每个人都着手实现自己的任务. 3.为了团队合作愉快进展顺利 ...