jetty加载spring-context容器源码分析

带着疑问开始

web.xml的顺序问题

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

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

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

//todo 以后贴出结果express

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

最简单的配置(这样不只产生两个容器并且每一个容器都生成同样的bean)设计模式

applicationContext:spring-mvc

spring-mvc-servlet.xmlmvc

正确的配置其中之一app

 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容器呢?jsp

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实例化的

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

相关文章
相关标签/搜索