WEB三大组件-servlet

简介

Java Servlet 是运行在 Web 服务器或应用服务器上的程序

在Java web b/s架构中,servlet扮演了重要的角色,作为一个中转处理的容器,他连接了客户端和服务器端的信息交互和处理

简单来说,客户端和服务器端的信息交互通常可以分为三个步骤:

  • servlet容器随时监听服务端口,当客户端发送请求到服务端,servlet容器将http请求报文包装成为request对象和一个空的response对象后,根据请求的url等信息再转发到具体的servlet
  • 对应的servlet在调用自身的service()方法来对请求进行处理,并且将响应的数据存放到response对象中
  • response传递到servlet容器,servlet容器负责将response对象转换成为响应报文之后再发送回客户端

大体流程如下图:

file

也可以参看此动画来理解:

img

生命周期

先了解一下web应用大概启动流程:

  • web应用启动,初始化一系列环境
  • xml解析,创建ServletContext实例
img

然后等待请求,如果所请求的Servlet如果还没有创建,就执行创建过程

  • xml解析,反射创建Servlet实例
  • xml解析,创建servletConfig实例
  • servletConfig作为形参来初始化Servlet

其中servlet从创建到毁灭所经历的过程如图所示

file

可以看到生命周期分为以下三个阶段:

  • 初始化阶段:会调用init()来进行初始化
    • 默认情况下,Servlet是在第一次被访问时由服务器创建的
    • 如果在web.xml中配置了的话,就会在服务器启动的时候创建Servlet
  • 服务阶段:请求到来时使用service()对外服务
    • 单实例多线程服务模式,针对每个请求都会从线程池中获得一个线程来处理请求
  • 销毁阶段:服务终止也就是销毁阶段,调用destroy()释放资源,标记为可回收等待回收

接口介绍

Servlet接口

file
  • Tomcat之所以放心地交给我们实现,是因为Servlet里主要写的代码都是业务逻辑代码。和原始的、底层的解析、连接等没有丝毫关系。最难的几个操作,人家已经给你封装成形参传进来了。
  • Servlet虽然是个接口,但实现类只是个空壳,我们写点业务逻辑就好了。

GenericServlet接口

file
file
  • 提升了init方法中原本是形参的servletConfig对象的作用域(成员变量),方便其他方法使用
  • init方法中还调用了一个init空参方法,如果我们希望在servlet创建时做一些什么初始化操作,可以继承GenericServlet后,覆盖init空参方法
  • 由于其他方法内也可以使用servletConfig,于是写了一个getServletContext方法
  • service未实现,留给用户自己实现

HttpServlet接口

file
file
  • 继承自GenericServlet

  • GenericServlet本身是一个抽象类,目的是是为了不让new

  • 实现了抽象方法service方法替我们完成了复杂的请求方法判断,我们后续只需要实现对应的方法即可

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String method = req.getMethod();
    long lastModified;
    if (method.equals("GET")) {
        lastModified = this.getLastModified(req);
        if (lastModified == -1L) {
            this.doGet(req, resp);
        } else {
            long ifModifiedSince = req.getDateHeader("If-Modified-Since");
            if (ifModifiedSince < lastModified) {
                this.maybeSetLastModified(resp, lastModified);
                this.doGet(req, resp);
            } else {
                resp.setStatus(304);
            }
        }
    } else if (method.equals("HEAD")) {
        lastModified = this.getLastModified(req);
        this.maybeSetLastModified(resp, lastModified);
        this.doHead(req, resp);
    } else if (method.equals("POST")) {
        this.doPost(req, resp);
    } else if (method.equals("PUT")) {
        this.doPut(req, resp);
    } else if (method.equals("DELETE")) {
        this.doDelete(req, resp);
    } else if (method.equals("OPTIONS")) {
        this.doOptions(req, resp);
    } else if (method.equals("TRACE")) {
        this.doTrace(req, resp);
    } else {
        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[]{method};
        errMsg = MessageFormat.format(errMsg, errArgs);
        resp.sendError(501, errMsg);
    }
 
}

参数介绍

file

也就是说,servletConfig对象封装了servlet的一些独有参数信息,servletContext对象封装了整个应用中所有servlet共享的参数信息。如果需要,我们可以从它们获取相应参数值。

ServletConfig

file

特点

  • 某个servlet独享
    • 只能被拥有改servletConfig的servlet获取,其他的servlet无法获取
  • 与Servlet是1对1关系
    • 当 Tomcat 初始化一个 Servlet 时,会将该 Servlet 的配置信息封装到 ServletConfig 对象中,此时可以通过调用 init(ServletConfig config)方法将 ServletConfig 对象传递给 Servlet。

方法说明

  • String getInitParameter(String name)
    • 根据初始化参数名返回对应的初始化参数值
  • Enumeration getInitParameterNames()
    • 返回一个 Enumeration 对象,其中包含了所有的初始化参数名
  • ServletContext getServletContext()
    • 返回一个代表当前 Web 应用的 ServletContext 对象
  • String getServletName()
    • 返回 Servlet 的名字,即 web.xml 中 <servlet-name>元素的值

ServletContext

file

ServletContext对象表示Servlet应用程序,每个web程序都只有一个ServletContext对象。因为有了ServletContext对象,就可以共享从应用程序中的所有资料处访问到的信息,并且可以动态注册Web对象,使得servlet之间传递和共享信息。前者将对象保存在ServletContext中的一个内部Map中。保存在ServletContext中的对象被称作属性。

特点
  • 全局共享
  • 服务器启动的时候创建,服务器关闭的时候销毁
  • 与Servlet是1对n关系

域对象方法

ServletContext作为域对象,同样有这三个域对象方法,用于对域属性进行获取或修改

  • setAttribute(String name,Object value)
    • 往域对象里面添加数据,添加时以key-value形式添加
  • getAttribute(String name)
    • 根据指定的key读取域对象里面的数据
  • removeAttribute(String name)
    • 根据指定的key从域对象里面删除数据使用方法

使用方法

配置初始化

在web.xml中配置全局参数

  <!-- 全局配置参数 -->
  <context-param>
         <param-name>param1</param-name>
         <param-value>value1</param-value>
  </context-param>

使用

获取参数值

  • getServletContext().getInitParameter(name)
    • 根据指定的参数名获取参数值
  • getServletContext().getInitParameterNames()
    • //获取所有参数名称列表

搜索当前工程目录下的资源文件

  • getServletContext().getRealPath(path)
    • 根据相对路径获取服务器上资源的绝对路径
  • getServletContext().getResourceAsStream(path)
    • 根据相对路径获取服务器上资源的输入字节流

获取工程名字

  • getServletContext().getContextPath();

ServletRequest & ServletResponse

ServletRequest是对一次HTTP请求的封装

ServletResponse是对一次HTTP响应的封装

此处就不展开叙述,具体可以参看之前的博文。

使用方法

使用一个servlet我们需要做两个方面的工作

  • 实现servlet类
  • 注册servlet

实现servlet类

任选一种方式实现Servlet类

  • 继承javax.servlet.Servlet类;
  • 继承javax.servlet.GenericServlet类;
  • 继承javax.servlet.HTTPServlet类;

然后实现对应的方法即可

注册servlet

注册servlet有两种方式

web.xml文件配置

在web.xml中将Servlet注册到容器中

<servlet>
         <!-- servlet的内部名称,自定义 -->
        <servlet-name>DemoAction</servlet-name>
        <!-- servlet的类全名:包名+类名 -->
        <servlet-class>com.uplooking.controller.DemoAction</servlet-class>
        <load-on-startup>1</load-on-startup>
</servlet>  
<!-- servlet的映射配置 -->
<servlet-mapping>
        <!-- servlet的内部名称,一定要和上面的内部名称保持一致 -->
        <servlet-name>DemoAction</servlet-name>
        <!-- servlet的映射路径(访问serclet的名称 -->
        <url-pattern>/DemoAction</url-pattern>
</servlet-mapping>
  • 首先,从浏览器中发送请求,是从当前工程中的路径与servlet-mapping标签中的url-pattern的标签值进行匹配。
  • 根据这个映射值,找到servlet-mapping标签中的servlet-name的值与servlet标签中的servlet-name进行匹
  • 匹配到以后,找到servlet标签中的servlet-class标签中对应servlet类的src文件夹下的全路径。
  • 从而调用并执行相应的servlet类。

注意:servlet-mapping标签中的servlet-name的值与servlet标签中的servlet-name必须相同

注解配置

只需在对应的servlet类中添加servlet注解即可,从浏览器发送请求时,是用当前“工程”下的路径,会去对应servlet类的上面寻找是否存在对应url名称的@webServlet注解,存在的话,调用并执行对应的servlet类。

 @WebServlet("/DemoAction")
 public class DemoAction  extends HttpServlet{
 }

是否单例

大部分情况下来说,一个servlet同一时间只有一个实例,但是,如果有多个Url映射到同一个servlet时,就会出现多个实例。所以虽然 Servlet 在多数情况下只有一个实例。但它并不是单例设计模式,即不是真正的单例。

线程安全问题

当多个请求发送到同一个servlet,服务器会为每个请求创建一个新进程来处理

因为会多线程处理,所以就可能访问到公共资源造成线程不安全的情况,所以我们在写代码的时候使用到公共变量的时候需要慎重考虑线程安全问题

参考文章

JavaWeb—Servlet基础(细节版,相当细节)

java基础学习:JavaWeb之Servlet

Servlet(上)

servlet/tomcat等容器/springMVC之间的关系

servlet/Tomcat/ Spring 之间的关系

JavaWeb——Servlet(全网最详细教程包括Servlet源码分析)

ServletContext介绍及用法

ServletConfig对象与ServletContext区别

servlet两种配置方法详解

本文由博客群发一文多发等运营工具平台 OpenWrite 发布

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342