深入分析Java Web技术内幕-6 Servlet工作原理解析

带着如下问题学习本文内容:
1、以Tomcat为例,说说Servlet容器是如何工作的;
2、一个Web工程是如何在Servlet容器中启动的;
3、Servlet容器是如何解析你的web.xml中定义的Servlet;
4、用户的请求是如何被分配给指定的Servlet的;
5、Servlet容器是如何管理Servlet生命周期的;
6、Servlet的API类层次结构,以及如何分析Servlet中的一些难点问题;

1 Servlet容器

Servlet容器作为一个独立发展的标准化产品,其存在的意义是为了适应工厂化生产,通过标准化接口来相互协作,实现Servlet和Servlet容器之间的解耦。
tomcat容器模型如下图所示:
在这里插入图片描述
由上图可知,Tomcat的容器分为4个等级,真正管理Servlet容器的是Context容器,一个Context对应一个Web工程。

1.1 Servlet容器的启动过程

当添加一个应用时将会创建一个StandardContext容器,并且给这个容器设置一些必要的参数,如:url和path分别代表这个应用在tomcat的访问路径和这个应用的实际物理路径,另外一个非常重要的配置是ContextConfig,这个类将会负责整个web应用配置的解析工作,最后将这个context容器加到父容器Host中。

tomcat主要类的启动时序图如下所示:
在这里插入图片描述
由上图可知:tomcat的通过start()方法启动tomcat,而tomcat的启动逻辑是基于观察者模式设计的,所有的容器都会继承lifecycle接口,由他管理容器的整个生命周期,所有容器的修改和状态的改变都会由他去通知已经注册的观察者。

当context容器初始化状态为init时,添加在context容器的listener将会被触发,而ContextConfig继承了LifecycleListener接口,因此它也就被加入到StandardContext容器中,负责整个web应用的配置文件解析工作。

ContextConfig的init方法主要完成以下工作:

  • 创建用于解析xml配置文件的contextDigester对象
  • 读取默认context.xml配置文件,解析
  • 读取默认host配置文件,解析
  • 读取默认context配置文件,解析
  • 设置context的docBase

ContextConfig的init方法完成后,context容器会执行startInternal方法,主要完成以下任务:

   1、创建读取资源文件的对象
    
    2、创建ClassLoader对象
    
    3、设置应用的工作目录
    
    4、启动相关的辅助类如:logger realm resources等
    
    5、修改启动状态,通知感兴趣的观察者(web应用的配置)
    
    6、子容器的初始化
    
    7、获取ServletContext并设置必要的参数
    
    8、初始化"load on startup"的servlet
    

1.2 Web应用的初始化工作

Web应用的初始化工作是在ContextConfig的configureStart方法中实现的,主要是为了解析web.xml文件。

在web.xml文件中的各个配置项将会被解析成相应的属性,保存在WebXml对象中。

接下来会将WebXml对象中的属性设置到Context容器中,这里包括创建Servlet对象、filter、listener等。

提问:为什么要将servlet包装成StandardWrapper?
因为StandardWrapper是tomcat容器中的一部分,具有容器的特征,而servlet是一个独立的web开发标准,不应该强耦合到tomcat中。

除了将servlet包装成StandardWrapper并作为子容器添加到context中,其他的所有web.xml属性都被解析到context中,所以说context容器才是真正运行servlet的容器,一个web应用对应一个context容器,容器的配置属性由应用的web.xml指定。

2 创建Servlet实例

2.1 创建Servlet对象

如果Servlet的load-on-startup配置项大于0,那么在Context容器启动时就会被实例化。

创建Servlet实例的方法是从Wrapper.loadServlet开始的,loadServlet方法要完成的就是获取servletClass,然后把它交给InstanceManager去创建一个基于servletClass.class的对象,如果这个Servlet配置了jsp-file,那么这个servletClass就是在conf/web.xml中定义的org.apache.jasper.servlet.JspServlet了。
创建Servlet对象的相关类结构图如下所示:
在这里插入图片描述

2.2 初始化Servlet

初始化Servlet在StandardWrapper的initServlet方法中,通过调用Servlet的init方法,同时把包装了StandardWrapper对象的StandardWrapperFacade作为ServletConfig传给Servlet。

 private synchronized void initServlet(Servlet servlet) throws ServletException {
        if (!this.instanceInitialized || this.singleThreadModel) {
            try {
                if (Globals.IS_SECURITY_ENABLED) {
                    boolean success = false;

                    try {
                        Object[] args = new Object[]{this.facade};
                        SecurityUtil.doAsPrivilege("init", servlet, classType, args);
                        success = true;
                    } finally {
                        if (!success) {
                            SecurityUtil.remove(servlet);
                        }

                    }
                } else {
                    servlet.init(this.facade);
                }

                this.instanceInitialized = true;
            } catch (UnavailableException var10) {
                this.unavailable(var10);
                throw var10;
            }
            //省略catch
        }
    }

3 Servlet体系结构

Servlet顶层类关联图如下:
在这里插入图片描述
与Servlet主动关联的是ServletConfig、ServletRequest和ServletResponse。这三个类都是通过容器传递给Servlet的,其中ServletConfig在Servlet初始化时就传递给Servlet了,ServletConfig中的方法主要是为了获取这个Servlet的一些配置属性,而这些属性可能在Servlet运行时被用到;
ServletContext可以认为是一个全局的存储信息的空间,所有用户共享一个ServletContext,tomcat启动就存在,tomcat关闭才释放;
ServletRequest和ServletResponse是实现数据交互的具体对象,可以理解为运输工具用来传递交互结果;

ServletConfig和ServletContext在Tomcat容器中的类关系图:
在这里插入图片描述
可以看出,StandardWrapper和StandardWrapperFacade都实现了ServletConfig接口,而StandardWrapperFacade是StandardWrapper门面类,所以传给Servlet的是StandardWrapperFacade对象,这个类能够保证从StandardWrapper中拿到ServletConfig所规定的数据,而又不把ServletConfig不关心的数据暴露给Servlet。

同样,ServletContext和ServletConfig也有类似的结构,在Servlet中能拿到的ServletContext的实际对象也是ApplicationContextFacade对象,ApplicationContextFacade同样保证ServletContext只能从容器中拿到它该拿的数据,这是典型的门面设计模式

我们在创建自己的Servlet类时通常使用的是HttpServletRequest和HttpServletResponse,它们继承了ServletRequest和ServletResponse。从Context容器中传过来的ServletRequest和ServletResponse可以直接被转化为HttpServletRequest和HttpServletResponse。
在这里插入图片描述
通过分析Tomcat的Request和Response类结构图可以得知:Tomcat一接收到请求首先会创建org.apache.coyote.Request和org.apache.coyote.Response,用来将请求快速分配给后续线程去处理,然后用户线程去处理这个请求时又创建org.apache.catalina.connector.Request和org.apache.catalina.connector.Response对象,直到传给Servlet,但是实际上传给Servlet的是Request和Response的门面类RequestFacade和ResponseFacade,所以我个人认为这也就解释了为何Context容器传过来的ServletRequest、ServletResponse可以被转化为HttpServletRequest和HttpServletResponse了。

4 Servlet如何工作

当用户从浏览器向服务器发起一个请求通常会包含以下信息:http://hostname:port/contextpath/servletpath,其中:hostname和port用来与服务器建立TCP连接,后面的URL用来选择在服务器中哪个子容器来服务用户的请求。
Tomcat是如何实现根据URL来到达正确的Servlet容器中的呢?
在Tomcat7中通过一个专门的映射类来完成,这个映射类就是org.apache.tomcat.util.http.mapper,这个类保存了Tomcat的Container容器中的所有子容器的信息,org.apache.catalina.contnector.Reqeuest类在进入Container容器之前,Mapper将会根据这次请求的hostname和contextpath将host和context容器设置到Request的mappingData属性中,所以当Request进入Container容器之前,对于它要访问哪个子容器就已经确定了。
在这里插入图片描述
之所以Mapper中会有容器的完整关系,还是多亏了MapperListener类,其核心方法如下:

public void startInternal() throws LifecycleException {
        this.setState(LifecycleState.STARTING);
        Engine engine = this.service.getContainer();
        if (engine != null) {
            this.findDefaultHost();
            this.addListeners(engine);
            Container[] conHosts = engine.findChildren();
            Container[] arr$ = conHosts;
            int len$ = conHosts.length;

            for(int i$ = 0; i$ < len$; ++i$) {
                Container conHost = arr$[i$];
                Host host = (Host)conHost;
                if (!LifecycleState.NEW.equals(host.getState())) {
                    this.registerHost(host);
                }
            }

        }
    }

这段代码的作用就是将MapperListener类作为一个监听者加到整个Container容器的每个子容器中,这样只要任何一个容器发生变化,MapperListener都将会被通知到,在for循环中就是将host及下面的子容器注册到mapper中。

请求到达最终的Wrapper容器后,接下来就是要执行Fiter链以及通知你在web.xml中定义的listener,然后执行Servlet的service方法。

当servlet从容器中移除时,也就意味着servlet的生命周期结束了,这次servlet的destroy方法将被调用。

5 Servlet中的Listener

Listener是基于观察者模式设计的,能够方便地从另一个纵向维度控制程序和数据。
详见:https://yq.aliyun.com/articles/636239?type=2

6 Filter如何工作

Fiter简介

Fiter虽然不是一个Servlet,不能产生response,但是它能够在一个request到达servlet之前预处理request,也可以在response离开servlet时处理response;
主要用途是:比如过滤一些敏感内容、编码过滤、URL权限控制等等。

工作原理

在这里插入图片描述

生命周期

Filter接口中有三个重要的方法:

init()方法:初始化参数,在创建Filter时自动调用。当我们需要设置初始化参数的时候,可以写到该方法中。

doFilter()方法:拦截到要执行的请求时,doFilter就会执行。这里面写我们对请求和响应的预处理。

destroy()方法:在销毁Filter时自动调用,当web容器调用这个方法之后,容器还会再调用一次doFilter方法。

Filter的创建和销毁由web服务器来控制。

  • 服务器启动的时候,web服务器创建Filter的实例对象,并调用其init方法,完成对象的初始化功能,filter对象只会创建一次,init方法也只会执行一次;
  • 拦截到请求时,执行doFilter方法,可以多次执行;
  • 服务器关闭时,web服务器销毁Filter的实例对象。

Filter类的核心还是FilterChain对象,这个对象保存了到最终Servlet对象等待所有Filter对象,这些对象都保存再ApplicationFilterChain对象的filters数组中,再FilterChain链上每执行一个Filter对象,数组的当前计数都会加1,直到计数等于数组的长度。当FilterChain上所有的Filter对象执行完成后,就会执行最终的servlet,所以在ApplicationFilterChain对象中会持有Servlet对象的引用。

Servlet中的url-pattern

在web.xml中和中都有配置项,其作用是匹配一次请求是否会执行这个Servlet或者Filter。
Filter的url-pattern匹配是在创建ApplicationFilterChain对象时进行的,它会把所有定义的Filter的url-pattern与当前的URL匹配,如果匹配成功就将这个Filter保存到ApplicationFilterChain的filters数组中,然后在FilterChain中依次调用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
GeoPandas是一个开源的Python库,旨在简化地理空间数据的处理和分析。它结合了Pandas和Shapely的能力,为Python用户提供了一个强大而灵活的工具来处理地理空间数据。以下是关于GeoPandas的详细介绍: 一、GeoPandas的基本概念 1. 定义 GeoPandas是建立在Pandas和Shapely之上的一个Python库,用于处理和分析地理空间数据。 它扩展了Pandas的DataFrame和Series数据结构,允许在其中存储和操作地理空间几何图形。 2. 核心数据结构 GeoDataFrame:GeoPandas的核心数据结构,是Pandas DataFrame的扩展。它包含一个或多个列,其中至少一列是几何列(geometry column),用于存储地理空间几何图形(如点、线、多边形等)。 GeoSeries:GeoPandas中的另一个重要数据结构,类似于Pandas的Series,但用于存储几何图形序列。 二、GeoPandas的功能特性 1. 读取和写入多种地理空间数据格式 GeoPandas支持读取和写入多种常见的地理空间数据格式,包括Shapefile、GeoJSON、PostGIS、KML等。这使得用户可以轻松地从各种数据源中加载地理空间数据,并将处理后的数据保存为所需的格式。 2. 地理空间几何图形的创建、编辑和分析 GeoPandas允许用户创建、编辑和分析地理空间几何图形,包括点、线、多边形等。它提供了丰富的空间操作函数,如缓冲区分析、交集、并集、差集等,使得用户可以方便地进行地理空间数据分析。 3. 数据可视化 GeoPandas内置了数据可视化功能,可以绘制地理空间数据的地图。用户可以使用matplotlib等库来进一步定制地图的样式和布局。 4. 空间连接和空间索引 GeoPandas支持空间连接操作,可以将两个GeoDataFrame按照空间关系(如相交、包含等)进行连接。此外,它还支持空间索引,可以提高地理空间数据查询的效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值