设为首页 收藏本站
查看: 961|回复: 0

[经验分享] jetty加载spring-context容器源码分析

[复制链接]

尚未签到

发表于 2017-3-1 08:52:00 | 显示全部楼层 |阅读模式
带着疑问开始

web.xml的顺序问题
  先拿一个最简单的spring mvc web.xml来说问题,如下图:如果我将三者的顺序倒置或是乱置,会产生什么结果呢?
  启动报错?还是加载未知结果?还是毫无影响?
DSC0000.png

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

applicationContext.xml和spring-mvc-servlet.xml怎么配置
  最简单的配置(这样不仅产生两个容器而且每个容器都生成一样的bean)
  applicationContext:
DSC0001.png

  spring-mvc-servlet.xml
DSC0002.png

  正确的配置其中之一



1 applicationContext.xml
2 <?xml version="1.0" encoding="UTF-8"?>
3 <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4        xmlns:context="http://www.springframework.org/schema/context"
5        xmlns="http://www.springframework.org/schema/beans"
6        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
7       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
8     <!--正确的配置-->
9     <context:component-scan base-package="com.meituan.jetty.spring">
10         <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
11     </context:component-scan>
12 </beans>
13  
14 spring-mvc-servlet.xml
15 <beans xmlns="http://www.springframework.org/schema/beans"
16        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
17        xmlns:context="http://www.springframework.org/schema/context"
18        xsi:schemaLocation="http://www.springframework.org/schema/beans
19         http://www.springframework.org/schema/beans/spring-beans.xsd
20         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
21     <!--正确的配置-->
22     <context:component-scan base-package="com.meituan.jetty.spring.controller"/>
23     <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
24         <property name="prefix" value="/"/>
25         <property name="suffix" value=".jsp"/>
26     </bean>
27 </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接口的少部分实现
DSC0003.png


深入源码解决疑难

为什么不管怎么配置app.xml总是在mvc之前初始化
  下边让我们用源码一步一步来分析其中的奥妙

启动jetty容器入口
  ......................前边还有一大段代码
DSC0004.png

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

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


  •   ContextLoaderListener

  •   DispatcherServlet


ContextLoaderListener和DispatcherServlet的实质(插播)
  contextLoaderListerner实质是实现了EventListener的一个事件监听器
  相关事件通知,在我的另外一篇wiki中有详细介绍:
  事件通知机制深入源码#事件通知机制深入源码-ApplicationListener
  或者 直接看这里:Spring事件通知机制详解
DSC0006.png


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



DispatcherServlet 的父类FrameworkServlet的方法initServletBean会被回调
DSC0008.png    这时候我们还要知道一个事情:contextLoaderListerner 和 DispatcherServlet 是在spring的两个package里边,前者在spring-web里边,后者在spring-webmvc里边,这个对后边的理解有帮助


启动jetty容器入口(续)
  为什么listener能够无论以哪种姿态都会优先于servlet执行呢?
  要解决这个问题,我们先看下listener是在何时被回调的:
  首先大概浏览下这个图,这里对WebAppContext和ContextHandler大概有一个映像(当然,这个是jetty的源码)
DSC0009.png

  jetty启动,会初始化一个WebAppContext(WebAppContext 继承了 ServletContextHandler ,ServletContextHandler  继承了ContextHandler ,而且他们都实现了 )对象;
  最终,WebAppContext对象的startContext()方法会被实现,如下图调用链:
DSC00010.png

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

DSC00012.png

  我们发现,ContextHandler里边存了一个listener的集合,而恰巧我们的 ContextLoaderListener 实例也在这个集合当中;
  我们看到这里把ContextLoaderListener和event事件传递给了callContextInitialized方法,所以ContextLoaderListener的contextInitialized方法最终会被调用,
  到此为止,我们就解释了ContextLoaderListener是会被合理的初始化的;
  至于ContextLoaderListener初始化的详细过程,请看这里:浅谈jetty如何初始化spring容器-ContextLoaderListener初始化context容器的过程
  咦?好像有什么不对的地方。哦,对,本来是WebAppContext的startContext方法,怎么会跑到ContextHandler的startContext方法,看上图,
  是子类父类的关系,原来如此;
  看调用链,再来说说 boot 本来调用server的start,为什么会走到lifeCycle呢?
DSC00013.png

  原来 server 继承了 AbstractLifeCycle,jetty源代码里边大量运用了 模板方法和类模板方法,我们开发的时候也可以学习这种设计模式,减少重复代码,提高代码复用率。
  讲了这么多,还没讲到 为什么 listener 总是在 servlet之前执行呢?
  莫急,且听下边讲解
  如下,WebAppContext的 doStart 方法被调用,此时WebAppContext自己实现了一部分,其余直接调用父类->ContextHandler的doStart方法
  (咦,不对,父类不是ServletContextHandler么;哦,ServletContextHandler并未重写这个方法)
DSC00014.png

  接下来调用ContextHandler的doStart方法
DSC00015.png

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

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

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

  对!父类这个方法里边初始化了 ContextLoadListener ,也就是初始化了所有的 事件通知 !!!
  事件通知完成之后,开始调用servlet的initialize方法,初始化servlet;servlet初始化详解:深入浅出jetty初始化spring容器-DispatcherServlet初始化context容器的过程
DSC00019.png   也就是说:frameworkServlet初始化方法的回调是由ServletContextHandler的startContext方法引起的!!!

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

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

ContextLoaderListener初始化context容器的过程

ContextLoaderListener结构
DSC00022.png


ContextLoaderListener入口
  首先,回调ContextLoaderListener的contextInitialized方法
DSC00023.png

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

  紧接着在1的时候创建context
DSC00025.png

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

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

  strategy又是怎么初始化的呢?
DSC00028.png

  look
DSC00029.png

DSC00030.png

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

  最终调用 AbstractApplicationContext的refresh方法,根据配置文件内容,开始初始化;
  AbstractApplicationContext的refresh的初始化都知道吧?不知道的话,可以看我这篇关于spring初始化顺序的文章:Spring Init&Destroy#spring容器的主要入口

DispatcherServlet初始化context容器的过程

DispatcherServlet结构
DSC00032.png


DispatcherServlet入口
DSC00033.png

  初始化webApplicationContext,
DSC00034.png

  创建webApplicationContext
DSC00035.png

  这里的contextClass是这么决定的
DSC00036.png

  最终也是调用refresh实例化的
DSC00037.png

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

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-348576-1-1.html 上篇帖子: Winows Azure运行Jetty- 下篇帖子: Jersey +jetty 实现微服务(一)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表