jetty加载spring-context容器源码分析
带着疑问开始
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容器到底是怎样的
- 两个容器分别是怎么初始化的呢?
- 为什么applicationContext容器就是mvc context容器的父容器呢?
- 这两个容器分别是什么类型的applicationContext实现呢?(我们知道applicationContext是接口)
下边的图是applicationContext接口的少部分实现
深入源码解决疑难
为什么不管怎么配置app.xml总是在mvc之前初始化
下边让我们用源码一步一步来分析其中的奥妙
启动jetty容器入口
......................前边还有一大段代码
初始化调用的是mms server的start方法,其实server没有start方法,是它的父类AbstractLifeCyle的start方法,然后再回调,我们来看下server的结构
在继续讲server是怎么一步步调用之前,我们需要知道两个事情
ContextLoaderListener
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容器源码分析的更多相关文章
- Spring IOC 容器源码分析 - 获取单例 bean
1. 简介 为了写 Spring IOC 容器源码分析系列的文章,我特地写了一篇 Spring IOC 容器的导读文章.在导读一文中,我介绍了 Spring 的一些特性以及阅读 Spring 源码的一 ...
- Spring IOC 容器源码分析系列文章导读
1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本.经过十几年的迭代,现在的 Spring 框架已经非常成熟了.Spring ...
- Spring IOC 容器源码分析 - 余下的初始化工作
1. 简介 本篇文章是"Spring IOC 容器源码分析"系列文章的最后一篇文章,本篇文章所分析的对象是 initializeBean 方法,该方法用于对已完成属性填充的 bea ...
- Spring IOC 容器源码分析 - 填充属性到 bean 原始对象
1. 简介 本篇文章,我们来一起了解一下 Spring 是如何将配置文件中的属性值填充到 bean 对象中的.我在前面几篇文章中介绍过 Spring 创建 bean 的流程,即 Spring 先通过反 ...
- Spring IOC 容器源码分析 - 循环依赖的解决办法
1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的.在本篇文章中,我会首先向大家介绍一下什么是循环依赖.然后,进入源码分析阶段.为了更好的说明 Spring 解决循环依赖的办法,我 ...
- Spring IOC 容器源码分析 - 创建原始 bean 对象
1. 简介 本篇文章是上一篇文章(创建单例 bean 的过程)的延续.在上一篇文章中,我们从战略层面上领略了doCreateBean方法的全过程.本篇文章,我们就从战术的层面上,详细分析doCreat ...
- Spring IOC 容器源码分析 - 创建单例 bean 的过程
1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑.对于已实例化好的单例 bean,getBean(String) 方法并不会再一次去 ...
- 最简 Spring IOC 容器源码分析
前言 BeanDefinition BeanFactory 简介 Web 容器启动过程 bean 的加载 FactoryBean 循环依赖 bean 生命周期 公众号 前言 许多文章都是分析的 xml ...
- Spring Boot JDBC:加载DataSource过程的源码分析及yml中DataSource的配置
装载至:https://www.cnblogs.com/storml/p/8611388.html Spring Boot实现了自动加载DataSource及相关配置.当然,使用时加上@EnableA ...
- Spring IOC 容器源码分析
声明!非原创,本文出处 Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 S ...
随机推荐
- js/jquery 实时监听输入框值变化的完美方案:oninput & onpropertychange
(1) 先说jquery, 使用 jQuery 库的话,只需要同时绑定 oninput 和 onpropertychange 两个事件就可以了,示例代码: $('#username').bin ...
- T-SQL 公用表表达式(CTE)
公用表表达式(CTE) 在编写T-SQL代码时,往往需要临时存储某些结果集.前面我们已经广泛使用和介绍了两种临时存储结果集的方法:临时表和表变量.除此之外,还可以使用公用表表达式的方法.公用表表达式( ...
- FastSocket.Net
Overview FastSocket是一个轻量级易扩展的c#异步socket通信库,项目开始于2011年,经过近3年不断调整与改进,目前在功能和性能上均有不错的表现. 项目地址:https://gi ...
- NFC 与 Windows Phone 的那点事儿
说起NFC这个词儿应该已经不陌生了,在我们的生活中有很多使用场景都是使用的这项技术,例如公交卡,门禁,还有银联的闪付卡等等.并且近些年在移动设备上使用的场景也越来越多,例如 对 NFC TAG 的读写 ...
- mysql同主机数据库复制
我们有时候需要在测试环境和生产环境下做交换.以下可以快速的复制数据库,支持大容量(1G以上) 1.将database_DEV数据库导成database_DEV.contents.gz压缩文件 mysq ...
- 关于同一台机器上安装多个sql实例的连接方法
由于客户需要在一台服务器上安装了两个sql服务器(一个sql2000,一个是sql2005,其实例名不同),默认的端口1433被先安装的sql2000使用,后来安装的的随机启用了一个3045端口.其中 ...
- 常用CSS Reset汇总
什么是Css Reset呢? 在 HTML标签在浏览器里有默认的样式,不同浏览器的默认样式之间也会有差别.在切换页面的时候,浏览器的默认样式往往会给我们带来麻烦,影响开发效率.所以解决的方法就是一开始 ...
- ef 更新导航属性
总之,要让所有的东西,都被DbContext管理状态 1.查出来,改了,再提交 2.new 出来,attach,再改,再提交 以上两种都较好理解 3.new出来,改了,再attach,在改状态,再提交 ...
- vuejs入门小demo-搜索大全
这个demo非常适合入门的同学,不再是简单的todolist.用到的知识点有组件通信,过渡效果,vue-rsource,还有一些基本的vue指令. 先放一张截图: 是不是感觉高端大气上档次呢,演示地址 ...
- [团队项目]SCRUM项目4.0
1.准备看板. 形式参考图4. 2.任务认领,并把认领人标注在看板上的任务标签上. 先由个人主动领任务,PM根据具体情况进行任务的平衡. 然后每个人都着手实现自己的任务. 3.为了团队合作愉快进展顺利 ...