Servlet

前言

Servlet在我工作中其实是一个每天都要接触的东西,毕竟是搞Java Web的。但是Tomcat、Spring MVC都已经帮我们做好了所有的东西,所以我们在用的时候,只是简单的写了Class,再写个注解就能使用了。所以属于比较容易遗忘的东西。这里就简单的讲解一下什么是Servlet,我个人认为也没有必要弄得非常深和全面(其实我也没有那个技术),毕竟别人都滚好轮子了。我们用就是了,但是还是要理解一下其中原理。遇到问题也好查找原因。

什么是Servlet

Servlet在Java Web开发中每天打交道的一个东西,它是什么?简单的说我认为它只是一个规范、一个接口,就像BeanFactory一样定义的规范类似。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package javax.servlet;

import java.io.IOException;

public interface Servlet {
void init(ServletConfig var1) throws ServletException;

ServletConfig getServletConfig();

void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

String getServletInfo();

void destroy();
}

这就是Servlet的接口规范。其实很简单。依次说下每个方法的作用,我们就能了解Servlet是干什么的

  • init 初始化servlet 其实就是我们经常配的<init-params>,有了他我们才能知道这个Servlet对应哪个请求,找准自己的定位
  • getServletConfig 不讲了,没撒用,获取配置
  • service 处理请求,
  • getServletInfo get方法没撒好说的
  • destory 销毁servlet

所以上面可以看出其实Servlet主要就规范了3个东西,接受请求、处理请求、响应请求。

那么Servlet这么简单。我们就实现一个Servlet是不是就可以用了?不行!这只是一个规范,所以还需要一个容器来管理Servlet。请求来了帮我们找到指定的Servlet。比如我们最最最常用的Tomcat(所以Tomcat也可以说是一个Servlet容器)。其实这里有点像Spring和Bean之间的关系。

HttpServlet

讲了Servlet,那我们到底怎么使用。这里就引入了HttpServlet。毕竟Servlet这个接口太抽象了。我们不好用,所以他就有了模板类,就是我们经常会用到的模板方法模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* @author: zl
* @Date: 2019/12/9 23:39
*/
public class MyServlet extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
super.doGet(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}

}

HttpServlet实现了Servlet接口(中间还有一个抽象类,你可以理解它对Servlet又做一步实现的扩充),也把能做的事情都做了。留了doXXX接口给我们实现。让我们写自己的逻辑请求。上面就是我们常用的Servlet的实际写法。其实我们还要配置ServletConfig,不然这个Servlet都不知道自己是谁。比如可以在Web.xml中配置初始化的基本参数

至于请求是怎么进入Servlet的,这个到SpringBoot的启动里面讲一下,因为SpringBoot是内嵌Tomcat,所以看源码比较好看一点。而且好理解。

Servlet生命周期

Servlet的生命周期很多时候面试可能都会去问,其实当简单看懂上面的说明后加上一些工作经验和猜测,就可以简单的猜出它的生命周期了。

servlet生命周期

  1. 客户端发起Http请求
  2. 容器去解析请求
  3. 创建Servle,注意这里其实是有Tomcat容器肯定不会每次都初始化,在第一次初始化会将这个Servlet保存在内存中,下次请求就可以直接使用了
  4. 调用Init方法,初始化请求
  5. 调用Servce方法,响应请求
  6. 输出响应信息,一般会帮我们封装成Response
  7. 调用destory()方法,由于Servlet会驻留在内存中。一般情况是tomcat容器关闭会去调用destory方法

Jsp和Servlet

说到Servle还是必须说明一下Jsp。Jsp是在Servlet后面出现的。主要是因为Servlet在对返回Html页面的时候不是很友好,所以才有了Jsp。
所以我们可能都知道Jsp其实就是一个Servlet

其实我们在访问一个Jsp的时候,它在第一次访问的时候。会将Jsp编译成一个Java类,这个类是继承也是HttpServlet的。

比如自己可以做个测试环境
做一个简单jsp

1
2
3
4
5
6
7
8
9
10
11
12
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
hello world
</body>
</html>

当我们调用通过url获取jsp页面时,其实已经讲Jsp编译成Class,实际上就是执行Servlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package org.apache.jsp.WEB_002dINF.jsp;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;

public final class hello_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
// 省略
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
try {
javax.servlet.jsp.JspWriter out = null;

response.setContentType("text/html; charset=UTF-8");
pageContext = _jspxFactory.getPageContext(this, request, response,
null, true, 8192, true);
_jspx_page_context = pageContext;
application = pageContext.getServletContext();
config = pageContext.getServletConfig();
session = pageContext.getSession();
out = pageContext.getOut();
_jspx_out = out;
out.write("\r\n");
out.write("<!DOCTYPE html>\r\n");
out.write("<html lang=\"en\">\r\n");
out.write("<head>\r\n");
out.write(" <meta charset=\"UTF-8\">\r\n");
out.write(" <title>Title</title>\r\n");
out.write("</head>\r\n");
out.write("<body>\r\n");
out.write(" hello world\r\n");
out.write("\r\n");
out.write("</body>\r\n");
out.write("</html>");
// ...
}
}

我只断断续续的贴了一部分。其中HttpJspBase的基本关系:public abstract class HttpJspBase extends HttpServlet implements HttpJspPage,可以看到其实也是继承了HttpServlet,那他是怎么把html给客户端的,可以看到实际也就是通过write()写出去。其实和我们一样的。

不过Jsp相对于Servlet好用的一点是,它内置了9个对象:out、session、response、request、config、page、application、pageContext、exception我们可以直接在jsp中使用(其实我用的很少,主要也就获取一个项目根路径,然后el表达式基本够用)。

Jsp也就基本结束了

题外话,为了测试演示了一下,用SpringBoot搞一个Jsp环境好多坑,也确实SpringBoot其实对Jsp不怎么友好,如果没有前后端分离想使用页面,还是推荐它默认的thymeleaf