关键字:

  Bootsrap,Catalina,Server,Service,Engine,Host,Context,Wrapper,Valve,Pipeline,ContextConfig,ServletConfig,ContextLoaderListener,DispatcherServlet.

一:环境配置

  我用的分析的工具是eclipse,源码用maven的自动下载源码的功能.然后在maven中添加相应的dependence.这样但我们ctrl+leftmouse的时候就会自动帮我们下载源代码.

  SpringMVC版本为3.2.4,虽然现在基本用的是Spring4.x或是SpringBoot,但基本的原理没有变动太大所以有典型性.

  Tomcat的版本为8.0.47.

  servlet-api的版本为3.1.0.

二:Tomcat的基本原理

  1.调用在Bootstrap中的主函数main()程序入口调用自身的start()方法,在start()方法中实例化org.apache.catalina.startup.Catalina类调用其start()方法,在Catalina的start()中调用load()载入tomcat文件夹目录下conf文件夹下的server.xml并创建Server类,然后调用Server的start()方法,到这里我们的tomcat就运行起来了.

  2.Server类代表整个Tomcat服务器,这里有必要介绍一下为什么Tomcat叫Servlet容器,在Tomcat中包含四个容器,分别为Engine,Host,Context,Wrapper.Engine是Host的父容器,依次类推.四者都继承自Container接口.一般在Tomcat中一个Engine实例中包含一个Host实例,一个Host实例中包含一个Context实例,一个Context代表一个WEB程序并包含多个Wrapper实例,一个Wrapper代表一个Servlet类.

  3.一次请求的大题流程是这样的,首先由Connector获取到Http请求,封装ServletRequest和ServletResponse对象并派发给Context管道中的所有Valve,执行完所有Valve后执行基础Valve根据请求的url映射到对应的Wrapper,然后调用个Wrapper管道中的所有Valve,最后调用基础Valve在如这个Wrapper类所封装的Servlet,实例化后调用器Service方法对用户的请求进行处理,最后将结果负载在response中返回给用户.

三:从源码开始分析Tomcat运行原理

 1.Bootsrap作为整个Tomcat的入口中的main()方法,doemon为Bootstrap类型.

  

   

  在start()中载入并实例化Catalina并调用了start()方法.

  

  在start()中调用load()方法载入了Server类,然后调用了其start()方法启动Tomcat.

  

 2.下面让我们仔细分析Tomcat是怎么以优雅的方式配置Server类的.

  

  在createStartDigester内部

  

  

 这里先解释一下Digester是一个用于解析xml的小框架,内部通过封装sax解析来实现的.介绍一下Digester的功能:

  A.在遇到patter对应的标签时创建对应的对象,将对象压栈,然后标签结束后再将这个对象出栈.

  

  

  通过上面的代码配置在遇到<Server>标签时创建StandardServer对象,并将该对象压进Digester内部的stack中,遇到</Server>的时候将StandardServer对象出栈.

  B.开启对应标签的attribute属性填充到实例化的类中的功能.

  

  C.这是最关键的地方,在遇到对应的匹配规则后在end()的方法调用栈顶下面紧挨着的对象的指定的方法并将栈顶的对象作为参数穿进去.

  

    

  刚开始的时候我很迷惑,因为在整个程序中都没有看到Catalina什么时候显式的设置了Server类型的属性,后来我才发现这个秘密:还是在Catalina的load()方法中.

   

  在真正开始Digester的parse()方法之前先将自身压进了自身的栈中,然后我们再看下面

  

  我们结合Tomcat中conf目录下的server.xml文件,<Server>就是根元素.所以解析最后出栈的肯定是<Server>标签对应的StandardServer对象.我接着看这几行代码.

   

  因为最开始将Catalina类自身以this传了进来所以栈底是Catalina对象,<server>标签结束的时候会调用Ctalina的setServer()方法将StandardServer对象的实例作为参数传进来.到这里我们成功设置了server属性,调用StandardServer的start()方法.

   

 3.StandardServer中的start()方法继承自LifecycleBase然后又调用自己的startInternal().

  

  

  在startInternal()中启动了由Digester从server.xml中解析出来的默认的Service组件

   

  再看一下Catalina中的代码

  

  遇到<Service>后会实例化StandardService实例然后在解析</Service>结束后会将StandardService的实例添加到StandardServer实例中.

 4.StandardService中一次调用了StandardEngine和StandardHost的start()方法,但是我们始终找不到StandardContext是什么时候被初始化的让我们看看程序是怎么创建StandardContext实例的.

  

  还是回到Catalina初始化Digester实例时的代码,我们看对于<Host>标签加的一些规则

  

  这里的比较核心的就是HostConfig,它继承自LifecycleListenerRule,当StandardHost启动的时候会调用监听器的启动事件的处理逻辑.

  我们认真对待这块,当调用StandardHost的start()方法实际上调用的是父类LifecycleBase的start()方法.

  

  这里调用子类的startInternal()方法,StandardHost和ContainerBase都实现了这个方法,根据多态规则首先会调用最底层子类的方法

  

  然后在这里又会调用父类ContainerBase类的startInternal()方法,让我们看这个方法.

  

  这个方法会出发生命周期start的状态.

  

  在setStateInternal()的方法中获取上面的枚举类型的LifecycleState中String类型的成员变量,STARTING类型对应Lifecycle.START_EVENT类型的事件,然后看fireLifecycleEvent()方法.

  

  这里将实践类型传给了LifecycleSupport中维护的LifecycleListener类型的集合,并调用其中核心方法lifecycleEvent().

  所以回到上面创建Digester的时候我们定义了将匹配规则,在解析到<Host>的时候会开始实例化HostConfig类型的LifecycleListener并将其添加到StandardHost当中.然后在start()方法调用后会触发HostConfig中的lifecycleEvent()方法.

  

  调用start()方法.

   

  在start()中调用了deployApps()方法.

  

  让我们直接看Tomcat我们部署的项目的载入方式,项目的文件夹为webapps.

  

  向Digester解析生成的线程池中提交一个DeployDirectory类的实例.

    

  在里面又调用了HostConfig类的deployDirectory()方法.

  

  在deployDirectory()方法中前面对路径进行了一系列的判断

  

  让我们看看这个路径.

  

  有时候我们的的项目的相关目录下并没有这个文件.

  

  所以直接进入了else块直接生成了StandardContext实例初始化参数后直接添加到了父容器StandardHost中.

 5.StandardContext类的初始化

  让我们先看看在HostConfig中是怎么配置StandardContext的.

  

  首先先给他添加了一个生命周期事件监听器.

  

  在StandardContext中我们找到了这个监听器,ContextConfig.想必到这里大家如果看懂前面HostConfig的执行原理,这个也不难理解,在这里我就不仔细的介绍了,当Context启动的时候肯定会出发start类型的时间,然后调用ContextConfig的lifecycleEvent()方法.让我们看看在这个ContextConfig中都做了什么.

  

  

  调用configureStart()方法中调用webConfig()载入我们WEB-INF文件夹下面的web.xml.

  

  这里也是用Digester进行解析的,添加的匹配规则无非是对Servlet,Filter等我们在开发中经常用到的配置标签的解析并将生成的实体类填充在WebXml类型的实例中.还有一点值得提示的是在第一次获取ServletContext的时候会对其进行初始化为ApplicationContext.

  

  最后在configureContext方法中将填充完毕的WebXml实例中的相关数据填充到Context中.

  

  下面的代码接着上面的.

  

  这里看Servlet填充的过程,用Wrapper包装Servlet再将这些Wrapper填充到Context中.

  让我们接着看在StandardContext的startInternal()我们比较感兴趣的地方.

  

  这里会将以字符串数组保存的listener数组进行初始化.

  

  对实例化的listener进行分类

  

  

  对初始化的监听器分类我们这里关心ServletContextListener类型的监听器,判断如果符合该类型的监听器后便会加入方法中临时创建的名为lifecycleListeners的ArrayList中.

  

  设置刚生成的lifecycleListenders.

   

  生成事件,调用ServletContextListener类型实例的contextInitialized()方法,这就是Tomcat和SpringMVC勾搭开始的地方.具体的内容我们在下一篇讲,睡了.

  

  

  

  

  

  

  

  

  

Tomcat与SpringMVC结合分析(一)的更多相关文章

  1. SpringBoot中Tomcat和SpringMVC整合源码分析

    概述 ​ SpringBoot中集成官方的第三方组件是通过在POM文件中添加组件的starter的Maven依赖来完成的.添加相关的Maven依赖之后,会引入具体的jar包,在SpringBoot启动 ...

  2. Servlet和Tomcat底层源码分析

    Servlet 源码分析   Servlet 结构图 Servlet 和 ServletConfig 都是顶层接口,而 GenericServlet 实现了这两个顶层接口,然后HttpServlet ...

  3. Tomcat 8.5 架构分析

    官方文档:Apache Tomcat 8 Architecture 以下分析的是 Version 8.5. Tomcat 组件关系图 根据 Architecture Overview 绘制: Serv ...

  4. spring boot 加载web容器tomcat流程源码分析

    spring boot 加载web容器tomcat流程源码分析 我本地的springboot版本是2.5.1,后面的分析都是基于这个版本 <parent> <groupId>o ...

  5. tomcat与springmvc 结合 之---第17篇 StandContext容器和SpringMVC的WebApplicationContext的联系

    writedby 张艳涛, 上一篇分析了,dispatcherservlet通过getServletConfig 方法获取了web.xml定义的<param-init>属性的过程 那么在如 ...

  6. 三思考,实现自己定义404页:Tomcat、SpringMVC精确匹配、重写DispatchServlet

    第1种方式:Tomcat直接处理 web.xml <error-page> <error-code>404</error-code> <location> ...

  7. springmvc(一) springmvc框架原理分析和简单入门程序

    springmvc这个框架真的非常简单,感觉比struts2还更简单,好好沉淀下来学习~ --WH 一.什么是springmvc? 我们知道三层架构的思想,并且如果你知道ssh的话,就会更加透彻的理解 ...

  8. IDEA+Tomcat+Maven+SpringMVC基于Java注解配置web工程

    1.在IDEA中新建Maven工程,使用archetype. 2.添加Maven依赖 <dependencies> <dependency> <groupId>ju ...

  9. SpringMVC学习(一)———— springmvc框架原理分析和简单入门程序

    一.什么是springmvc? 我们知道三层架构的思想,并且如果你知道ssh的话,就会更加透彻的理解这个思想,struts2在web层,spring在中间控制,hibernate在dao层与数据库打交 ...

随机推荐

  1. 小白的Python之路 day1 Python3的bytes/str之别

    原文:The bytes/str dichotomy in Python 3 Python 3最重要的新特性大概要算是对文本和二进制数据作了更为清晰的区分.文本总是Unicode,由str类型表示,二 ...

  2. Android测试:Fundamentals of Testing

    原文地址:https://developer.android.com/training/testing/fundamentals.html 用户在不同的级别上与你的应用产生交互.从按下按钮到将信息下载 ...

  3. django中request相关用法

    URL相关信息: HttpRquest对象包含当前请求url的一些信息,通过这些信息,你可以获得正在访问这个页面的用户,或者使用的浏览器: request.path :除域名以外的请求路径,以正斜杠开 ...

  4. 4、libgdx应用框架

    (原文:http://www.libgdx.cn/topic/29/4-libgdx%E5%BA%94%E7%94%A8%E6%A1%86%E6%9E%B6) 模块 作为核心.libgdx提供了六个接 ...

  5. Android 訪问权限清单

    Android权限设置 概述 权限 说明 訪问登记属性 android.permission.ACCESS_CHECKIN_PROPERTIES 读取或写入登记check-in数据库属性表的权限 获取 ...

  6. 自学Python4.2 迭代器、生成器

    迭代器.生成器一.迭代器 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退,不过这也没什么, 因为人们很少在迭代途中往后退.另外 ...

  7. myeclipse 2014 customize_Perspective 失效解决方法-有效

    1.将9个jar复制到myeclipse安装目录\plugins中 2.删除和这9个jar同包名但是版本号较低的9个文件 3.重启myeclipse 2014 三步走: 到这个地址下载 http:// ...

  8. oracle 主键自增 设置----杜恩德

    <div id="topicList"> <div class="forFlow"> <div class = "pos ...

  9. python scipy计算机数值库

    scripy有聚类的python 代码,如: http://blog.csdn.net/elaine_bao/article/details/50242867

  10. 其他函数:值为NULL时的默认值NVL,DECODE

    NVL(列,默认数字值),此函数返回值为数值型,非NULL时返回原始值,NULL时返回默认数字值. DECODE: