目录

  • `Servlet` 简述
  • `Servlet` 接口
  • `Servlet` 的生命周期和请求处理逻辑
  • 使用 `Servlet` 开发示例
    • 实现 `Servlet` 接口
    • 继承 `GenericServlet` 抽象类
    • 继承 `HttpServlet` 抽象类
    • `HttpServlet` 抽象类的设计
  • `Servlet` 细节
    • `Servlet` 与单例
    • 多个映射路径
    • 映射路径通配符
    • `Servlet` 映射路径优先级
    • `JspServlet`
    • 默认的 `Servlet`
    • `Servlet` 初始化时机

Servlet 简述

Servlet 是运行在实现了 Java Servlet 规范的服务器端一个小程序,是 Java Web 的三大组件之一(ServletListener 监听器、Filter 过滤器),它属于动态资源,能够动态的根据请求的信息返回不同的响应

Servlet 是我们深入学习 SpringMVC 框架的基础

Servlet 接口

javax.serlvet.Servlet 接口是 Servlet 规范中最顶级的接口。此接口定义了初始化 servlet、为请求提供服务以及从服务器中删除 servlet 的方法。这些方法称为 Servlet 的生命周期方法,它们按照一定顺序调用

package javax.servlet;
import java.io.IOException;public interface Servlet {/*** servlet 容器在实例化 servlet 后调用 init 方法一次进行初始化。只有完成该方法(不抛出异常),然后servlet才能接收任何请求* @param config 包含 servlet 的配置和初始化参数的 ServletConfig 对象*/public void init(ServletConfig config) throws ServletException;/*** 返回一个 ServletConfig 对象,该对象包含此servlet的初始化和启动参数,返回的ServletConfig对象是传递给init方法的参数对象* 此接口的实现负责存储ServletConfig对象,以便此方法可以返回它,实现此接口的 GenericServlet 类已经执行了此操作** @return 辅助初始化servlet的ServletConfig对象。*/public ServletConfig getServletConfig();/*** 由 servlet容器(Web 服务器)调用,以允许servlet处理和响应请求。只有在servlet的 init()方法成功完成后,才能调用此方法** @param req 包含客户端请求的 ServletRequest 对象* @param res 包含 servlet 响应的 ServletResponse 对象*/public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException;/*** 返回有关 servlet 的信息,如作者、版本和版权。* 此方法返回的字符串为纯文本,而不是任何类型的标记(如 HTML、XML等)语言。** @return 返回有关 servlet 的信息字符串*/public String getServletInfo();/*** 由 servlet(Web 服务器)容器调用,以指示 servlet 正在停用* 当服务器正常关闭时会调用此方法,使servlet有机会清理正在保留的任何资源(例如, 内存、文件句柄、线程)* 在 servlet 容器调用此方法后,它将不会再在此 servlet 上调用service方法。*/public void destroy();
}

上面的 Servlet 实例和方法都是由 Servlet 容器(如 tomcat 服务器)调用的,因此我们的项目不需要任何 main 方法,也不需要创建任何 Servlet 实例

Servlet 的生命周期和请求处理逻辑

  • Web 服务器接收到 HTTP 请求,将请求转发给 servlet 容器,servlet 容器中根据请求路径查找对应的 Servlet
  • 如果 servlet 实例还未创建,那么就创建一个 servlet 实例,随后 servlet 容器调用 servletinit() 方法对 servlet 进行初始化(该方法只会在 servlet 第一次被载入时调用)。被创建的 servlet 实例会被保留在容器中,当后续有同样的请求到来时可以直接处理,不再需要重新创建和初始化
  • servlet 容器会解析到来的 HTTP 请求,调用该 servlet 实例的 service() 方法,以执行 service() 方法中的业务逻辑, service() 方法就是我们要重点开发的方法
  • 如果 Servlet 实例被销毁,那么将会由 Servlet 容器执行 destory() 方法,通常都是在服务器关闭时 Servlet 才会销毁,destory() 方法可以进行资源的释放

使用 Servlet 开发示例

我们使用 Servlet 时,需要对 Servlet 进行实现。一般来说,实现方式有 3

  • 实现 javax.servlet.Servlet 接口
  • 继承 javax.servlet.GenericServlet 抽象类
  • 继承 javax.servlet.http.HttpServlet 抽象类

实现 Servlet 接口

@WebServlet("/first-servlet")
public class FirstServlet implements Servlet {@Overridepublic void init(ServletConfig servletConfig) {System.out.println("---------------");System.out.println("servletConfig: " + servletConfig);System.out.println("init");}@Overridepublic ServletConfig getServletConfig() {System.out.println("---------------");System.out.println("getServletConfig");return null;}/*** 我们主要开发的方法** @param servletRequest  请求对象* @param servletResponse 响应对象*/@Overridepublic void service(ServletRequest servletRequest, ServletResponse servletResponse) throws IOException {//获取请求的数据String message = servletRequest.getParameter("xx");//封装响应数据servletResponse.setContentType("text/html");// HelloPrintWriter out = servletResponse.getWriter();out.println("<html><body>");out.println("<h1>" + message + "</h1>");out.println("</body></html>");}@Overridepublic String getServletInfo() {System.out.println("getServletInfo");return null;}@Overridepublic void destroy() {System.out.println("destroy");}
}

想要成功运行 servlet 还要进行 servlet 的路径配置。这里,我们使用 servlet 3.0 开始提供的 @WebServlet 注解来配置 servlet 的路径。当然也可以使用 web.xml 来配置,但是这比较麻烦。这里的路径必须使用 “/” 开头,并且相对的是当前应用根目录

如果是配置 web.xml,那么可以这样配置

<servlet><!--3、 servlet的内部名称--><servlet-name>firstServlt</servlet-name><!--4、 servlet的类全路径名--><servlet-class>com.example.servlet_01.FirstServlet</servlet-class>
</servlet>
<!-- 映射 -->
<servlet-mapping><!--2、 servlet 的映射内部名称,通过他可以找到 servlet 的内部名称,不区分大小写--><servlet-name>firstServlt</servlet-name><!--1、 请求 servlet 的映射路径--><url-pattern>/first-servlet</url-pattern>
</servlet-mapping>

继承 GenericServlet 抽象类

java.servlet.GenericServlet 类使编写 servlet 变得更容易,它实现了 ServletServletConfig 接口的方法(除了 service() 方法)。GenericServlet 类也实现了 log() 方法,这是一个在 ServletContext 接口中定义的方法,同时具有自己的 init()log() 方法

Generic 意为通用的,自己编写的 servlet 直接扩展 GenericServlet 类比实现 Servlet 接口更加方便

@WebServlet("/generic-servlet")
public class MyGenericServlet extends GenericServlet {@Overridepublic void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {// 获取请求的数据String message = req.getParameter("xx");// 封装响应数据res.setContentType("text/html");// HelloPrintWriter out = res.getWriter();out.println("<html><body>");out.println("<h1>" + message + "</h1>");out.println("</body></html>");}
}

继承 HttpServlet 抽象类

实际上 GenericServlet 作为一个通用的 Servlet 实现,只提供了基本的功能,在某些情况下仍然不方便,比如我们的 Web 应用基本上都是基于 HTTP 协议的请求,因此非常有必要创造一个专门针对 HTTP 协议的 Servlet,比如处理各种请求,获取请求头信息等等

Java 已经为我们提供了这样的一个 Servlet 实现,那就是javax.servlet.http.HttpServletHTTP 的请求方式包括 DELETE、GET、OPTIONS、POST、PUT、TRACE,在 HttpServlet 类中分别提供了相应的服务方法,它们是 doDelete()、doGet()、doOptions()、doPost()、doPut()、doTrace()

public abstract class HttpServlet extends GenericServlet {protected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException{String protocol = req.getProtocol();String msg = lStrings.getString("http.method_get_not_supported");if (protocol.endsWith("1.1")) {resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);} else {resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);}}protected void doPost(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {String protocol = req.getProtocol();String msg = lStrings.getString("http.method_post_not_supported");if (protocol.endsWith("1.1")) {resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);} else {resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);}}protected void doPut(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {String protocol = req.getProtocol();String msg = lStrings.getString("http.method_put_not_supported");if (protocol.endsWith("1.1")) {resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);} else {resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);}}protected void doDelete(HttpServletRequest req,HttpServletResponse resp)throws ServletException, IOException {String protocol = req.getProtocol();String msg = lStrings.getString("http.method_delete_not_supported");if (protocol.endsWith("1.1")) {resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);} else {resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);}}
}

当通过继承 HttpServlet 来创建一个 Servlet 时,我们只需要根据要处理的请求的类型,来重写不同的方法就可以了,处理 get 请求,则重写 doGet(),处理 post 请求,则重写 doPost(),无需实现其他方法,开发 Servlet 时更加简单

@WebServlet("/http-servlet")
public class MyHttpServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 获取请求的数据String message = req.getParameter("xx");// 封装响应数据resp.setContentType("text/html");// HelloPrintWriter out = resp.getWriter();out.println("<html><body>");out.println("<h1>" + message + "</h1>");out.println("</body></html>");}
}

HttpServlet 抽象类的设计

HttpServlet 类中提供了 service(HttpServletRequest, HttpServletResponse) 方法,这个方法是 HttpServlet 自己的方法,不是从 Servlet 继承来的

HttpServlet 重写自父类的 service(ServletRequest, ServletResponse) 方法中会把ServletReques t和 ServletResponse 强转成 HttpServletRequestHttpServletResponse,然后调用 service(HttpServletRequest, HttpServletResponse) 方法,这就不用自己去强转请求和响应对象了

@Override
public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException {HttpServletRequest  request;HttpServletResponse response;try {request = (HttpServletRequest) req;response = (HttpServletResponse) res;} catch (ClassCastException e) {throw new ServletException("non-HTTP request or response");}service(request, response);
}
protected void service(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {String method = req.getMethod();if (method.equals(METHOD_GET)) {long lastModified = getLastModified(req);if (lastModified == -1) {doGet(req, resp);} else {long ifModifiedSince;try {ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);} catch (IllegalArgumentException iae) {ifModifiedSince = -1;}if (ifModifiedSince < (lastModified / 1000 * 1000)) {                   maybeSetLastModified(resp, lastModified);doGet(req, resp);} else {resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);}}} else if (method.equals(METHOD_HEAD)) {long lastModified = getLastModified(req);maybeSetLastModified(resp, lastModified);doHead(req, resp);} else if (method.equals(METHOD_POST)) {doPost(req, resp);} else if (method.equals(METHOD_PUT)) {doPut(req, resp);} else if (method.equals(METHOD_DELETE)) {doDelete(req, resp);} else if (method.equals(METHOD_OPTIONS)) {doOptions(req,resp);} else if (method.equals(METHOD_TRACE)) {doTrace(req,resp);} else {String errMsg = lStrings.getString("http.method_not_implemented");Object[] errArgs = new Object[1];errArgs[0] = method;errMsg = MessageFormat.format(errMsg, errArgs);resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);}
}

通过这些细可以发现,HttpServlet 的目的是让我们在进行基于 HTTP 请求的 Web 开发时更加简单,让开发者把更多的精力放在业务逻辑上去,而不是放在网络请求的解析上

Servlet 细节

Servlet 与单例

Servlet 是单例的,在 web 容器当中,在服务器关闭之前,自始至终就是同一个 Servlet。即同时并发的请求也是由同一个 servlet 处理的。这样的好处是能够减少对象的创建,提升性能,但是我们在开发 Servlet 的时候需要注意线程安全问题,尽量不要定义有状态的全局变量

多个映射路径

一个 Servlet,可以绑定一个或多个映射路径,通过这些映射路径发送的请求都会到达该Servlet

对于 web.xml 可以如下配置,配置多个 servlet-mapping,它们的 servlet-name 一致,但是 url-pattern 不一致

<servlet><!--3、 servlet的内部名称--><servlet-name>firstServlt</servlet-name><!--4、 servlet的类全路径名--><servlet-class>com.example.servlet_01.FirstServlet</servlet-class>
</servlet><servlet-mapping><!--2、 servlet的映射内部名称,通过他可以找到servlet的内部名称,不区分大小写--><servlet-name>firstServlt</servlet-name><!--1、 请求servlet的映射路径--><url-pattern>/first-servlet1</url-pattern>
</servlet-mapping><servlet-mapping><!--2、 servlet的映射内部名称,通过他可以找到servlet的内部名称,不区分大小写--><servlet-name>firstServlt</servlet-name><!--1、 请求servlet的映射路径--><url-pattern>/first-servlet2</url-pattern>
</servlet-mapping>

或者在一个 servlet-mapping 中配置多个 url-pattern

<servlet-mapping><!--2、 servlet的映射内部名称,通过他可以找到servlet的内部名称,不区分大小写--><servlet-name>firstServlt</servlet-name><!--1、 请求servlet的映射路径--><url-pattern>/first-servlet3</url-pattern><url-pattern>/first-servlet4</url-pattern><url-pattern>/first-servlet5</url-pattern>
</servlet-mapping>

对于采用 @WebServlet 注解的方式,那就更简单了,该注解的 value 是一个 String 数组,因此我们直接传递多个路径地址的数组就行了

@WebServlet({"/generic-servlet","/generic-servlet2","/generic-servlet3"})

映射路径通配符

Servlet 映射路径可以使⽤通配符 * ,通配符可以匹配任何的字符串

  • 如果路径是 /* 结尾,则表示匹配具有指定前缀路径的所有的路径(优先级最高)。它会拦截所有的请求,包括访问直接静态资源、jsp 文件的请求
  • 如果路径是 *. 扩展名,则表示匹配后缀结尾的任意路径
  • 这两种格式不能混用

Servlet 映射路径优先级

  • 对于某个具体的访问路径,如果存在该路径的 Servlet 映射,那么该 Servlet 优先级最高
  • 如果不存在,那么具有 /* 结尾的通配符路径的 Servlet 优先级最高
  • 如果不存在,那么具有 *.后缀名 结尾的通配符路径的 Servlet 优先级最高
  • 如果不存在,那么最后是具有 / 默认路径通配符的 Servlet,它也被称为默认 Servlet

如果使用了 /* 通配符 Servlet,那么当该 Servlet 中存在请求 include、forward、redirect 到其他没有显示指定路径的资源时(比如转发到某个 jsp、静态资源等),那么这个请求将会一直在这个 /* 通配符 Servlet 中循环直到抛出异常,因为转发的路径并没有在第一步匹配到具体的 Servlet 的映射,进而又匹配到了这个通配符 Servlet 本身,而这个通配符 Servlet 中又再一次进行了转发

JspServlet

对于以 *.jsp 结尾的访问路径,默认是被一个在 tomcat 容器的 conf/web.xml 配置文件中配置的 JspServlet 处理的,名为 jsp

<servlet><servlet-name>jsp</servlet-name><servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class><init-param><param-name>fork</param-name><param-value>false</param-value></init-param><init-param><param-name>xpoweredBy</param-name><param-value>false</param-value></init-param><load-on-startup>3</load-on-startup>
</servlet>

它的映射如下

<servlet-mapping><servlet-name>jsp</servlet-name><url-pattern>*.jsp</url-pattern><url-pattern>*.jspx</url-pattern>
</servlet-mapping>

默认的 Servlet

Java Web 应用的一切请求都是在请求对应的 Servlet,动态的资源就不必说了,有自己的路径,对于静态资源,比如 html、css、js、图片等,虽然在请求 URL 中看起来是访问的静态资源的地址,但是实际上仍然是通过一个默认 Servlet 查找资源的,没找到就返回 404

默认 servlet 也是在 tomcat 容器在 conf/web.xml 配置文件中帮我们配置的,名为 default

<servlet><servlet-name>default</servlet-name><servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class><init-param><param-name>debug</param-name><param-value>0</param-value></init-param><init-param><param-name>listings</param-name><param-value>false</param-value></init-param><load-on-startup>1</load-on-startup>
</servlet>

它的映射如下

<servlet-mapping><servlet-name>default</servlet-name><url-pattern>/</url-pattern>
</servlet-mapping>

所以说,静态资源的请求,会被这个由 tomcat 为我们配置的 Default 拦截并处理,在该 Servlet 中,会尝试查找请求 URL 路径对应的静态资源,找到了就返回该资源,找不到就返回 404
  
如果在项目中自定义了映射路径为 /Servlet,那么会覆盖容器为我们配置的Default,转而走我们自己配置的 Servlet 的逻辑

Servlet 初始化时机

Servlet 在默认情况下,只有当对应的请求第一次到来时才会初始化,也可以配置在启动服务器时立即初始化某些 Servlet。这样的好处是,在第一次请求的时候不会再创建 Servlet 对象了,提升处理速度,缺点就是启动服务器时会更多的对象,可能会延长服务器启动时间

对于 web.xml 可以如下配置,在 servlet 标签中添加一个 load-on-startup 子标签,该标签用于指示容器是否应该在 web 应用程序启动的时候就加载这个 Servlet(实例化并调用其 init() 方法)

<servlet><servlet-name>firstServlt</servlet-name><servlet-class>com.example.servlet_01.FirstServlet</servlet-class><load-on-startup>1</load-on-startup>
</servlet>

标签中填写的是启动的优先级

  • 它的值必须是一个整数,当值为 0 或者大于 0 时,表示容器在应用启动时就加载并初始化这个 servlet。值越小,该 servlet 的优先级越高,应用启动时就越先加载
  • 如果该元素的值为负数或者没有设置,则容器会当 Servlet 被请求时再加载
  • 当值相同时,容器就会自己选择顺序来加载
  • 对于@WebServlet 注解,可以在 loadOnStartup 属性中配置该特性

Web开发之Servlet相关推荐

  1. JavaEE Web开发之Servlet篇

    一.Servlet基础 a)   Servlet和JSP的关系 1. Servlet是一个运行在服务器端的Java程序,它可以动态地生成web页面,是属于客户和服务器响应的中间层.可以说JSP就是一个 ...

  2. Java服务器接口快速开发之Servlet详细教程

    今日科技快讯 7月14日晚间消息,据乐视网刚刚披露的业绩预告,公司预计2017年上半年亏损6.37亿至6.42亿元,上年同期盈利2.84亿元.报告期内,乐视网资产减值损失计提规模较大约为2.3亿元,其 ...

  3. 移动web开发之rem布局(rem基础、媒体查询、 less 基础、rem适配方案)

    移动web开发之rem布局 一.rem基础 rem单位 rem (root em)是一个相对单位,类似于em,em是父元素字体大小. 不同的是rem的基准是相对于html元素的字体大小. 比如,根元素 ...

  4. 17. 【移动Web开发之flex布局】

    文章目录 [移动Web开发之flex布局]前端小抄(17) 一.flex布局体验 1.1 传统布局与flex布局 1.2 初体验 二.flex布局原理 2.1 布局原理 三.flex布局父项常见属性 ...

  5. 「学习笔记」移动Web开发之flex布局9

    「学习笔记」移动Web开发之flex布局9 一.flex布局体验 1.1 传统布局与flex布局 1.2 初体验 二.flex布局原理 2.1 布局原理 三.flex布局父项常见属性 3.1 常见父项 ...

  6. WEB开发之HTML与CSS够用即可-庞永旺-专题视频课程

    WEB开发之HTML与CSS够用即可-113人已学习 课程介绍         讲解常用的HTML标签与CSS样式.这些常用的HTML标签与CSS样式都是本人多年从业经验的总结.只要熟练我总结的HTM ...

  7. 移动WEB开发之-REM(rem)布局

    目录 移动WEB开发之REM布局 rem基础 rem单位 媒体查询 什么是媒体查询 媒体查询语法规范 1.mediatype 查询类型 2.关键字 3.媒体特性 4.媒体查询书写规则 less 基础 ...

  8. Python Web开发之WSGI

    Python Web开发之WSGI WSGI(全称Web Server Gate Interface,Web服务器网关接口)是Python为了规范和简化Web服务开发过程,定义了一种Web服务器和应用 ...

  9. Swift Web 开发之 Vapor - 入门(一)

    简介 Vapor 是一个基于纯 Swift 构建出的 Web 开发框架,目前可以运行在 macOS 和 Ubuntu ,用于构建出漂亮易用的网站或者 API 服务. 官方称是用的最多的 Swift w ...

  10. Swift Web 开发之 Vapor - 模版 Leaf(三)

    模版引擎,对现在的 Web 开发极为重要,几乎所有主流 Web 框架都会支持一种或多种模版引擎,模版引擎可以分离用户界面和业务逻辑,工作原理主要是一种翻译,后端对特定的标记.语法.变量等渲染后再输送给 ...

最新文章

  1. Storm入门之第一章
  2. 深度学习(06)-- Network in Network(NIN)
  3. 来了!苹果二代AirPods 3月发布 全黑配色加入
  4. 电商页面设计排版没有思路?可临摹PSD分层模板,诠释基础版式大招帮你轻松搞定!
  5. python提高运行效率_提高CPU密集型任务执行效率——Python多进程介绍,内附实例代码...
  6. 每天坚持跑步到底会不会瘦呢?
  7. mysql误删除ibdata1以及日志ib_logfile*
  8. python处理期货数据_用Python下载并分析期货持仓数据
  9. 计算机操作系统第四版第一章知识点归纳总结
  10. mysql 显示 乱码_MySQL 中文显示乱码
  11. 企业级负载均衡LVS集群——DR模式下的(加权)轮询调度器、DR模式下的健康检测(ldirectord)
  12. mysql实习报告总结_MySQL数据库实训报告 实训心得
  13. 【PyTorch 自然语言处理】传统 NLP 快速回顾(计算语言学)
  14. 联想电脑虚拟化开启方法
  15. 郑州财经学院第54次全国计算机,郑州财经学院第二期教师博士班开班
  16. java3D实现空间立方体,纯CSS3实现一个旋转的3D立方体盒子
  17. Java项目:SSH自驾游管理系统
  18. intellij创建快捷方式到桌面
  19. Java实战项目之人力资源管理系统【源码+课后指导】_Java毕业设计/计算机毕业设计
  20. 30天免费试用 ▎(IDM) 极速下载工具

热门文章

  1. 阿里云云计算 25 SLB的原理
  2. Mac OS用Anaconda安装Jupyter Notebook
  3. 易筋SpringBoot2.1 | 第二篇:Spring Boot配置文件详解
  4. pycharm调试步骤(详细)
  5. 腾讯云安装javaweb环境
  6. 层次凝聚聚类算法(HAC)
  7. Dijkstra + 堆优化
  8. tensorflow学习笔记(1):使用入门
  9. MongoDB 在windows shell环境下的基本操作和命令的使用示例(三)
  10. h3c交换机堆叠(IRF)配置三步完成