博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Servlet工作原理解析与应用
阅读量:2490 次
发布时间:2019-05-11

本文共 8115 字,大约阅读时间需要 27 分钟。

1 Servlet原理分析

     要介绍Servlet则要从Servlet容器说起,因为Servlet是放在Servlet容器里面运行的,就类似于子弹是放在枪膛里才能打出去一样。(两者则是通过标准化的协议接口来连接 的)

1.1 Servlet容器

     Servlet容器独立发展,种类很多,各有定位和特点,比较流行的有JettyTomcat等,其中以Tomcat占据市场主流。

    1.1.1 Tomcat

      这里以主流的Tomcat为例,Tomcat作为一种Servlet容器,它是如何管理Servlet的呢?

     先看以下Tomcat容器结构图:

               

 先明白两点:

       (1)Servlet容器并不直接管理Servlet,而是通过中间层来实现(Context容器)。一个Servlet容器可以包含多个Context容器,每个Context容器对应管理了一个Web工程,分为多个Servlet,且被包装成Wrapper被管理。

       (2)为什么适用中间层(即出现了Context容器和Wrapper包装层)这种管理手段?

         第一:一个Servlet容器可以放多个Web工程,但是每个工程要被单独管理,所以就要借助Context容器单独管理Web工程。

         第二:Servlet是独立开发的Web标准,不应该和容器产生强关联性耦合,所以要借助具有容器特征的StandWapper包装成子容器(Wapper,也就是Servlet实例)添加到Context中管理。(所以,可以得出另一个结论:Context容器才是真正运行Servlet的Servlet容器)

1.1.2 Tomcat容器组件关系

一:组件等级划分      

Tomcat按功能划分为许多不同的组件,并可通过/conf/server.xml文件对其定义和配置,包括Server, Service, Connector,Engine,  Host,  Context……一般可分4个等级:

     1、顶级组件:位于配置层次的顶级,并且彼此间有着严格的对应关系,有Server和Service组件;

     2、连接器:连接客户端(浏览器或Web服务器)请求至Servlet容器,如Connector组件,

     3、容器:功能是处理传入请求的组件,并创建相应的响应。如Engine处理对一个Service的所有请求,Host处理对特定虚拟主机的所有请求,Context处理对特定web应用的所有请求;

     4、被嵌套的组件:位于一个容器当中,但不能包含其它组件;一些组件可以嵌套在任何Container中,而另一些只能嵌套在Context中。

二:组件关系 

      如果把Tomcat比较一个框架类,那么一个Server服务器表示一个Tomcat实例,通常一个JVM也只包含一个Tomcat实例,一个Server中可以有多个Service服务,Service可以包含一个或多个Connector用于连接Container容器中的一个Engine,连接器Connector通过特定端口接收客户端请求,并将其转发到关联的Engine;

      而Engine作为Servlet容器引擎,包含多个Host组件和其他组件,引擎接收并处理来自一个或多个连接器的请求,并辨别请求的HTTP首部信息以便确定发往哪个Context或Host(客户端通常使用主机名来标识他们希望连接的服务器),并将完成的响应返回到连接器,以便最终传输回客户端。

      一个Context上下文对应一个Web应用程序,即一个Web project,而一个Web工程包含多一个Wrapper(Servlet应用程序的包装类)。也就是说,在tomcat容器等级中,Context容器直接管理Servlet在容器中的包装类Wrapper,所以Context容器如何运行将直接影响servlet的工作方式。

1.1.3 Tomcat启动过程简述

     Tomcat启动时通过org.apache.catalina.startup.Tomcat这个启动类完成的,启动步骤如下:

    (1)创建实例

    (2)添加Web应用

    (3)启动Tomcat

    (4)调用Servlet

Tomcat tomcat = getTomcatInstance();  //1:创建实例File appDir = new File(getBuildDirectory(), "webapps/examples");  tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath());  //2:添加Web应用tomcat.start();                       //3:启动实例ByteChunk res = getUrl("http://localhost:" + getPort() +                        "/examples/servlets/servlet/HelloWorldExample");  assertTrue(res.toString().indexOf("

Hello World!

") > 0); //4:调用servlet

启动时序如下所示:

         

1.2 Servlet

1.2.1 Servlet简介

1: 根据自己的理解,Servlet是什么?作用?

     (是什么?)Servlet是实现了Servlet接口,并通过“请求-响应”模式来访问的服务端Java程序;

     (干什么的)主要用于开发动态Web资源;交互式的浏览和修改数据,动态的扩展Server的能力。

(1)接受请求,并与服务器资源进行通信;

(2)创建并返回一个基于客户端请求性质的动态内容HTML页面给客户端。

下面是一段Servlet的具体定义:

     Servlet是sun公司提供的一种用于开发动态web资源的技术,其API提供了servlet接口,若用户想要开发一个动态web资源,需要:

  1、编写一个实现servlet接口的Java类。
  2、把开发好的Java类部署到web服务器中。
  通常,按照习惯把实现了Servlet接口的java程序,称为Servlet。

    Servlet依赖于Servlet容器(如常用的Tomcat和Jetty),两者类似于枪和子弹,相互依存、相互增强,但又相互独立,通过标准化接口进行协调工作。

    Servlet容器独立发展,种类很多,各有定位,各有特点,比较流行的有Jetty、Tomcat等,其中以Tomcat占据市场主流。

2 Servlet生命周期

      servlet始于装入web服务器内存,并在web服务器终止或重装servlet时结束。servlet一旦被装入web服务器,一般不会从web服务器内存中删除,直至web服务器关闭或重装才会结束。

      (1)初始化:web服务器启动时或web服务器接收到请求时加载Servlet。执行init();

      (2)调用:运行service()方法,service()自动派遣运行与请求相对应的doGet()或doPost()方法;

      (3)销毁:停止服务器时调用destroy()方法,销毁实例。 

3 Servlet调用流程

请求流程如图:

               

结合着生命周期说,Servlet程序是由WEB服务器调用,web服务器收到客户端的Servlet访问请求后:

   (1)Web服务器接收到浏览器发送的请求之后,首先检查是否已经装载并创建了该Servlet的实例对象。

   (2)如果没有Servlet实对象,装载并创建该Servlet的一个实例对象,调用Servlet实例对象的init()方法初始化。

   (3)创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。

   (4)WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。
 

               

 1.2.2 Servlet体系结构

              

Servlet的运行模式是一个“握手型的交互式”运行场景,其中:

  • ServletContext主要用来描述场景,制造发生场景的;
  • ServletConfig主要用来描述交易制定的参数集合;
  • ServletRequest和ServletResponse是交互的具体对象,用来传输参数和执行结果。

其中ServletRequest和ServletResponse发生了一系列转化:其转变过程如下:

             

          tomcat接到请求首先将会创建org.apache.coyote.Request和org.apache.coyote.Response,这两个类是Tomcat内部使用的描述一次请求和相应的信息类,它们是一个轻量级的类,作用就是在服务器接收到请求后,经过简单解析将这个请求快速分配给后续线程去处理。

         接下来当交给一个用户线程去处理这个请求时又创建org.apache.catalina.connector.Request和org.apache.catalina.connector.Response对象。这两个对象一直贯穿整个Servlet容器直到要传给Servlet,传给Servlet的是Request和Response的Facade类。

其中ServletConfig和Request类均使用了门面设计模式

1.2.3 Servlet工作原理

简述过程如下: 

    1:用户通过浏览器向服务器发送一个请求url:  http://hostname:port/contextpath/servletpath; 其中,hostname和port用来与服务器简历TCP连接,contextpaht和servletpath路径用于选择服务器中对应子容器服务请求。

      2:如何选择子容器?使用org.apache.tomcat.util.http.mapper这个映射类保存Container容器中所有子容器信息,  org.apache.tomcat.catalina.connector.Request请求类进入Container容器之前,Mapper类就根据hostname和contextpath在mappingData属性中保存了host和Container容器之间的关系,可以根据这个映射关系选择子容器。

      3:Mapper类又如何保存这个映射关系呢?使用类似于观察者模式的方法,将MapperListener作为一个监听者加入Container容器的每个子容器,一旦容器发生变化,保存容器关系的MapperListener类的mapper属性也会修改更新。

 

              

         上面的Request路由图描述了一次Request请求是如何到达最终的Wrapper容器的,我们现在知道了请求如何到达正确的Wrapper容器,但是在请求达到最终的Servlet前还要完成一些步骤,必须要执行Filter链,以及通知你在web.xml中定义的Listener。

         接下来就要执行Servlet的service方法了。通常情况下,我们自己定义的servlet并不直接去实现javax.servlet.servlet接口,而是去继承更简单的HttpServlet类或者GenericServlet类,我们可以有选择的覆盖相应的方法去实现要完成的工作。

Servlet的确能够帮我们完成所有的工作了,但是现在的Web应用很少直接将交互的全部页面用Servlet来实现,而是采用更加高效的MVC框架来实现。这些MVC框架的基本原来是将所有的请求都映射到一个Servlet,然后去实现service()方法,这个方法也就是MVC框架的入口。

        当Servlet从Servlet容器中移除时,也就表明该Servlet的生命周期结束了,这时Servlet的destory方法将被调用,做一些扫尾工作。

2 Servlet应用

2.1 手写Servlet类

Step 1:继承HttpServlet类别

Step 2:重写doGet()或者doPost()方法            

import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;public class ApplicationServlet extends HttpServlet{    //处理HTTP GET请求    @Override    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {       doPost(req, resp);    }    //处理HTTP POST请求    @Override    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//第一种:返回渲染结果        resp.setContentType("text/html;charset=utf-8");        PrintWriter out = resp.getWriter();        out.println("");        out.println("Servlet06</head>");        out.println("");        out.print("我是返回值");        out.print(req.getParameter("name"));        out.println("");        out.println("");//第二种:重新定向到servlet02        resp.sendRedirect("s02");//第三种:请求分派        //获取请求分派器        RequestDispatcher dispatcher = req.getRequestDispatcher("servlet04");        //将请求转发至指定路径的资源        dispatcher.forward(req,resp);//第四种:添加传递参数        String str = "在Servlet05中存放请求域属性";        req.setAttribute("content",str);        //获取请求分派器        RequestDispatcher dispatcher = req.getRequestDispatcher("servlet06");        //将请求转发至指定路径的资源        dispatcher.forward(req,resp);//第五种:获取初始化参数信息        //获取ServletContext实例        ServletContext context = this.getServletContext();        //获取指定名称的web应用上下文初始参数的字符串值        String str = context.getInitParameter("appName");        resp.setContentType("text/html;charset=utf-8");        PrintWriter out = resp.getWriter();        out.print("获取ServletContext的初始化参数\"appName\"的字符串值为:" + str);    }    @Override    public void destroy() {     }    @Override    public void init() throws ServletException {      }}

Step 3:在web.xml文件中注册Servlet方法

HelloServlet
Servlet.HelloServlet
HelloServlet
/servlet/HelloServlet

2.2 Servlet装载问题

1.用Servlet容器自动装载已经注册的servlet,需在web.xml配置:<loadon-startup>1</loadon-startup>,数字越小,级别越高。

2.Servlet容器首次启动后,客户首次向Servlet发送请求。

3.Servlet类文件被更新后,重新装载Servlet。

整个生命周期,初始化init()方法只被执行一次;例子执行结果:

                    

<load-on-startup>1</load-on-startup>

<load-on-startup>2</load-on-startup>

Servlet内置对象与初始化

2.3 Servlet与九大内置对象对应关系

         

Servlet内置初始化参数

1.web.xml文件配置:

This is the description ofmy J2EE component
This is the display nameof my J2EE component
ServletInitParameter
servletInitParameter.ServletInitParameter
username
admin
password
123456

2.在servlet类中通过如下代码获取参数:   

public void init() throws ServletException {      this.setUsername(this.getInitParameter("username"));      this.setPassword(this.getInitParameter("password"));   }

2.4 Servlet路径跳转问题

相对路径和绝对路径

    

Servlet路径跳转


相对路径访问Servlet
绝对路径访问Servlet

相对访问路径使用"servlet/ServletPathDirection"前面不带”/”,绝对访问路径使用“/servlet/ServletPathDirection”,前面加上”/”表示当前根目录;

3模型总结:

3.1 模型1:

                    

3.2 模型2

MVC思想

                         

你可能感兴趣的文章
FFmpeg 新旧版本编码 API 的区别
查看>>
RecyclerView 源码深入解析——绘制流程、缓存机制、动画等
查看>>
Android 面试题整理总结(一)Java 基础
查看>>
Android 面试题整理总结(二)Java 集合
查看>>
学习笔记_vnpy实战培训day02
查看>>
学习笔记_vnpy实战培训day03
查看>>
VNPY- VnTrader基本使用
查看>>
VNPY - CTA策略模块策略开发
查看>>
VNPY - 事件引擎
查看>>
MongoDB基本语法和操作入门
查看>>
学习笔记_vnpy实战培训day04_作业
查看>>
OCO订单(委托)
查看>>
学习笔记_vnpy实战培训day06
查看>>
回测引擎代码分析流程图
查看>>
Excel 如何制作时间轴
查看>>
股票网格交易策略
查看>>
matplotlib绘图跳过时间段的处理方案
查看>>
vnpy学习_04回测评价指标的缺陷
查看>>
ubuntu终端一次多条命令方法和区别
查看>>
python之偏函数
查看>>