一、Servlet简介

Servlet类最终开发步骤:

  • 第一步:编写一个Servlet类,直接继承HttpServlet
  • 第二步:重写doGet方法或者doPost方法,重写哪个我说的算!
  • 第三步:将Servlet类配置到web.xml文件当中
  • 第四步:准备前端的页面(form表单),指定请求路径即可

  • Servlet是一个接口,在它的下面有GenericServlet

    和HttpServlet两个实现类

我们来了解一下GenericServlet:

  • 其中的GenericServlet实现了很多方法,只保留了一个抽象方法,就是我们经常用的service方法。

  • 这或许就是为了更简洁一点吧,我们每次不需要在像实现Servlet接口一样,将全部的抽象方法都实现。
  • 根据他的类图可知,HttpServlet继承了GenericServlet这个类,那么它为什么要继承GenericServlet呢?

我们来了解一下HttpServlet:

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;
try {
ifModifiedSince = req.getDateHeader("If-Modified-Since");
} catch (IllegalArgumentException var9) {
ifModifiedSince = -1L;
} if (ifModifiedSince < lastModified / 1000L * 1000L) {
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);
} }
  • 这段是HttpServlet中实现的Service方法的源码,我们可以清楚的了解到,Tomcat已经把这个Service写好了,我们只需要去实现doGet()doPost()等等这些请求的方法就可以了。
  • 因为这个Service会自己判断,我们进行访问的是什么请求,然后自动找到相应的方法进行处理。

二、两种设计模式

2.1适配器模式

适配器:适配器就是一种适配中间件,它存在于不匹配的二者之间,用于连接二者,将不匹配变得匹配,简单点理解就是平常所见的转接头,转换器之类的存在。

public interface MyInterface {
void m1();
void m2();
void m3();
void m4();
void m5();
void test();
//但是这个接口中我们常用的方法只有test(),我们在实现此接口的时候,还需要实现其他方法,很累赘。
//所以我们就需要一个适配器!
}
public abstract class Test implements MyInterface{
public void m1() {
} public void m2() {
} public void m3() {
} public void m4() {
} public void m5() {
}
//这是一个适配器
//在创建一个接口的实现类,我们将常用的方法设置为抽象的方法
//这样我们只需要继承该类,然后实现方法即可
public abstract void test();
}
  • 这样做的优点就是下次我们只需要去继承Test类,实现我们经常使用的test()方法就可以了。
  • 使我们的减少了冗余的代码量
  • 同时你也会发现这个就和GenericServlet去继承Servlet这个接口是一样的
  • 这就是适配器设计模式,其实很简单,也有利于我们更加理解抽象类的作用和接口的使用
public class Realize extends Test{
public void test() {
//这样我们就不需要再去实现接口中的 其他方法了
}
}

2.2 模板设计方法模式

模板方法模式:是一种行为设计模式, 它在超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。

  • 模板方法模式建议将算法分解为一系列步骤, 然后将这些步骤改写为方法, 最后在 “模板方法” 中依次调用这些方法。 步骤可以是 抽象的, 也可以有一些默认的实现。 为了能够使用算法, 客户端需要自行提供子类并实现所有的抽象步骤。 如有必要还需重写一些步骤 (但这一步中不包括模板方法自身)。

  • 认真的去阅读上面这段话:你就会发现他说将 算法进行分解,最后在模板中依次调用:

    • 此时的你是否想起我们刚开篇介绍的HttpServlet???
    • HttpServlet继承了 GenericServlet类,而GenericServlet类又实现了Servlet接口;HttpServlet里面的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;
try {
ifModifiedSince = req.getDateHeader("If-Modified-Since");
} catch (IllegalArgumentException var9) {
ifModifiedSince = -1L;
} if (ifModifiedSince < lastModified / 1000L * 1000L) {
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);
} }
  • 一大堆的ifelse ifelse 这不就是所谓的将算法分块提炼了出来吗?然后我们只需要去刻画doGet()doPost()等方法,程序的主框架是不变的,只是其中的细节需要我们自己去实现。
  • 我个人目前觉得和适配器模式比较相似,但略有不同之处。

三、Servlet对象的声明周期

3.1 Servlet对象是由谁来维护的?

  • Servlet对象的创建、以及对象上方法的调用、对象的销毁这个过程,我们JavaWeb程序员是无权干预的。你可以仔细想想你什么时候new出来过一个Servlet对象。
  • Servlet对象的声明周期是由Tomcat服务器负责的
  • Tomcat服务器我们又可以称之为:WEB容器
  • WEB容器来管理Servlet对象的死活

3.2 Servlet认知强化

  • 你还真可以自己new一个Servlet对象,但是并不受我们Tomcat管理,所以你自己new出来的Servlet对象,死活和Tomcat没有关系。
  • Tomcat创建的Servlet对象,这些Servlet都会被放到一个集合当中(HashMap),只有放到HashMap集合的Servlet才能够被Tomcat容器管理。
  • Tomcat容器底层应该有一个HashMap这样的集合,在这个集合当中存储了Servlet对象和请求路径之间的关系,我想此时你更能够想到为什么我们总是在xml中如此定义?

  • 我们可以将Servlet对象称之为假单例模式,因为Servlet只有一个对象呀。
  • 配置在web.xml对应Servlet程序的标签下添加<load-on-startup>1</load-on-startup>配置,指定改Servlet对象在Web应用启动时创建

3.3 关于Servlet中的方法

  • 通过Servlet中的方法,我们就可以了解到它的生命周期、运行机制。
  • 无参数构造方法、init方法只在第一次用户发送请求的时候执行

  • 只要用户发送一次请求,service方法必然会被Tomcat调用一次。

  • 关于Servlet类中方法的调用次数:

    • 构造方法只执行一次
    • init方法只执行一次
    • service方法:用户发送n次请求执行n次
    • destroy方法:只执行一次

什么时候使用destroy方法:

  • 通常在destroy方法当中,进行资源的关闭,马上对象要被销毁了,还有什么没有关闭的,抓紧时间关闭资源。

四、Servlet映射问题

  • 只要是前端发送的请求,就必须要写项目名!
  • 当我们进行转发的时候需要加上"/":request.getRequestDispatcher('/b');
  • 当我们在web.xml中配置路径时,需要加上<url-pattern>/helloServlet</url-pattern>

url-pattern匹配规则:

  • "/":弱的 全路径匹配
  • ".do":以特殊方式结尾的匹配
  • "/*":强的 全路径匹配
  • "/hello/*":有前缀的 所以匹配

五、HttpServletRequest

  • 首先HttpServletRequest这个接口实现了ServletRequest接口,那么他们两个有什么区别呢?

    • HttpServletRequest:这个名字带有http,所以它的里面就有一些关于对浏览器的处理方法;例如

5.1 GET请求参数

● 在浏览器的url里发送,以查询字符串方式拼接参数(?key=value&key1=value1)

● 使用Ajax,设置请求类型为GET,在请求url后拼接查询字符串

● 用form表单发送

    /**
* GET,发送的请求参数在请求行内,
* 请求行的信息会由Tomcat自动解码 (utf-8)
*/
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HTTPServletOne doGet");
// 1 获取请求参数
System.out.println("根据getParameter获取请求参数值 :"+req.getParameter("name"));
// 2获取所有参数名
Enumeration<String> paras = req.getParameterNames();
String s = null;
while (paras.hasMoreElements()) {
s = paras.nextElement();
System.out.println("根据getParameterNames得到的参数名:"+s +"参数值:"+ req.getParameter(s));
}
}

5.2 POST请求参数

● form表单传输,指定请求方式为POST,默认Content-Type为application/x-www-form-urlencoded

    /**
* 发送POST请求,请求参数都在请求体里
* 请求体的内容由Servlet进行解析,默认编码规范为ISO
* 必须指定请求头 Content-Type
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HTTPServletOne doPost");
// 0 设置请求处理字符集
req.setCharacterEncoding("utf-8");
// 1.获取请求参数
System.out.println("根据getParameter获取请求参数值 :"+req.getParameter("name"));
}

● Ajax传输,指定请求方式为POST,必须添加Content-Type为application/x-www-form-urlencoded;charset=utf-8

    /**
* 发送POST请求,请求参数都在请求体里
* 请求体的内容由Servlet进行解析,默认编码规范为ISO
*/
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("HTTPServletOne doPost");
// 0 设置请求处理字符集
req.setCharacterEncoding("utf-8");
// 1 获取JSON数据
// 2 reader输入流, 是从请求体开始读的
BufferedReader reader = req.getReader();
String s = null;
StringBuilder sb = new StringBuilder();
while ((s = reader.readLine()) != null) {
sb.append(s);
}
String json = sb.toString();
// 3 通过FastJson 工具进行解析
Person person = JSON.parseObject(json, Person.class);
System.out.println(person);
}

● 附上前端代码

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript">
function onGet() {
//ajax
var xhr = new XMLHttpRequest();
xhr.open("GET","http://localhost:8080/httpServlet01?name=123&age=12");
xhr.send();
}
function onPost() {
//ajax
//用GET方式发送的请求参数在请求行内,请求行内的信息会由Tomcat自动解码(utf-8)
//如果用POST请求,所有参数都放在了请求体里,
//但是请求体的内容不是Tomcat解析的, 是由Servlet进行解析的,默认的编码规范为ISO
//则需要声明请求头格式:application/x-www-form-urlencoded
//表单发送的话 会默认加上这个类型
var xhr = new XMLHttpRequest();
xhr.open("POST","http://localhost:8080/httpServlet01");
xhr.setRequestHeader("Content-Type","application/json;charset=utf-8")
xhr.send("{'name':'123'}");
}
function onPut() {
//ajax
var xhr = new XMLHttpRequest();
xhr.open("PUT","http://localhost:8080/httpServlet01");
xhr.send();
}
function onDelete() {
//ajax
var xhr = new XMLHttpRequest();
xhr.open("DELETE","http://localhost:8080/httpServlet01");
xhr.send();
}
</script>
</head>
<body>
<h1>Hello,Servlet!</h1>
<button onclick="onGet()">发送GET请求</button>
<button onclick="onPost()">发送POST请求</button>
<button onclick="onPut()">发送PUT请求</button>
<button onclick="onDelete()">发送DELETE请求</button>
</body>
</html>

六、结尾

  • 对于Tomcat的Servlet内容就总结这么多,若想深入学习等待后续更新。
  • 我将会继续更新关于Java方向的学习知识,感兴趣的小伙伴可以关注一下。
  • 文章写得比较走心,用了很长时间,绝对不是copy过来的!
  • 尊重每一位学习知识的人,同时也尊重每一位分享知识的人。
  • 你的点赞与关注,是我努力前行的无限动力。

Tomcat深入浅出——Servlet(二)的更多相关文章

  1. Tomcat深入浅出——Servlet(三)

    零.HttpServletRequest 上一篇已经介绍了这个接口,现在补充些内容 首先介绍一下作用域: jakarta.servlet.jsp.PageContext pageContext 页面作 ...

  2. Tomcat剖析(二):一个简单的Servlet服务器

    Tomcat剖析(二):一个简单的Servlet服务器 1. Tomcat剖析(一):一个简单的Web服务器 2. Tomcat剖析(二):一个简单的Servlet服务器 3. Tomcat剖析(三) ...

  3. 浅谈Tomcat和Servlet

    本文浅谈下对Tomcat和Servlet总体的理解,初学时有用过一段时间,但当时疲于应对如何xml配置和使用,对他们的理解就像是一个黑匣子.现在回顾一下帮助自己加深网络的理解.开始还是先推荐我看的文章 ...

  4. Tomcat架构解析(二)-----Connector、Tomcat启动过程以及Server的创建过程

    Connector用于跟客户端建立连接,获取客户端的Socket,交由Container处理.需要解决的问题有监听.协议以及处理器映射等等. 一.Connector设计   Connector要实现的 ...

  5. tomcat和servlet的关系

    一.什么是servlet? 处理请求和发送响应的过程是由一种叫做Servlet的程序来完成的,并且Servlet是为了解决实现动态页面而衍生的东西.理解这个的前提是了解一些http协议的东西,并且知道 ...

  6. Tomcat深入浅出(一)

    一.Tomcat简介 我们下载好Tomcat后需要配置一下Java环境:如果打开出现闪退得情况,首先是jdk 同时配置JRE_HOME Tomcat的一些关键目录: /bin:存放用于启动及关闭的文件 ...

  7. Tomcat深入浅出——Session与Cookie(四)

    一.Cookie 1.1 Cookie概念 Cookie:有时也用其复数形式 Cookies.类型为"小型文本文件",是某些网站为了辨别用户身份,进行Session跟踪而储存在用户 ...

  8. Tomcat深入浅出——Filter与Listener(五)

    一.Filter过滤器 1.1 Filter过滤器的使用 这是过滤器接口的方法 public interface Filter { default void init(FilterConfig fil ...

  9. Tomcat深入浅出——最终章(六)

    前言 利用了六天的时间,对Tomcat的内容进行了整理. 学习不易.整理也不易,自己坚持的更了下来,很不错. 希望每一位在努力前行的小伙伴,都要相信坚持就会有收获. 本次Tomcat就探寻到这里,明年 ...

随机推荐

  1. 【远程文件浏览器】Unity+Lua开发调试利器

    Remote File Explorer是一个跨平台的远程文件浏览器,用户通过Unity Editor就能操作运行在手机上的游戏或是应用的的目录文件.比如当项目打包运行到设备上时,可通过Remote ...

  2. .NET宝藏API之:OutputFormatter,格式化输出对象

    相信大家在项目中都用过统一响应参数模板. 先声明一个响应模板类: public class ResponseDto { public int code { get; set; } public str ...

  3. 【审视】Scrum Master的检查清单

    一般情况下,一个Scrum Master如果更多的是做组织会议.确保时间盒以及对流程中的障碍快速响应等事项的话,可以同时引导2-3个团队.在这种情况下,团队会在降低问题发生率的基础上提高一定的绩效. ...

  4. SoftPool:基于Softmax加权的池化操作 | 2021新文

    SoftPool使用softmax进行加权池化,能够保持特征的表达性并且是可微操作.从性能和准确率来看,SoftPool是目前的常规池化方法的一个不错的替代品   来源:晓飞的算法工程笔记 公众号 论 ...

  5. Windows下查找各类游戏存档路径

    我算是个比较爱打单机游戏的人,同时也是个半吊子的编程爱好者,有的时候会去干一些修改存档的事儿.不过这篇博文不讲存档修改技术,只讲第一步:去哪找存档? 目标:在windows10系统下搜索到游戏的存档路 ...

  6. CentOS 下 MySQL 8.0 安装部署,超详细!

    点击上方"开源Linux",选择"设为星标" 回复"学习"获取独家整理的学习资料! Mysql8.0安装 (YUM方式) 首先删除系统默认或 ...

  7. MySQL深入学习-day1

    书名<MySQL是怎样运行的:从根儿上理解MySQL>可自行百度 以下是知识点总结 重新认识Mysql MySQL是一个C/S架构的软件. 在Windows安装后首先注册成服务,然后会在开 ...

  8. linux篇-linux下源码安装nginx

    LNMP模式 后续继续更新,先搭建nginx 安装环境gcc gcc-c++ 2 下载源码包解压 配置第一个报错 安装openssl openssl-devel yum -y install open ...

  9. MOS管实现的STC自动下载电路

    目录 MOSFET, MOS管基础和电路 MOS管实现的STC自动下载电路 三极管配合 PMOS 管控制电路开关 STC MCU在烧录时, 需要断电重置后才能进入烧录状态, 通常是用手按开关比较繁琐. ...

  10. nginx 部署前端资源的最佳方案

    前言 最近刚来一个运维小伙伴,做线上环境的部署的时候,前端更新资源后,总是需要清缓存才能看到个更新后的结果.客户那边也反馈更新了功能,看不到. 方案 前端小伙伴应该都知道浏览器的缓存策略,协商缓存和强 ...