1. 什么是 Servlet?

2. Servlet 类的继承关系

3. Servlet 实现方式

4. Tomcat 容器等级

5. Servlet 执行流程

6. Servlet 生命周期

7. Tomcat 装载 Servlet 的三种情况

8. forward 和 redirect

9. Jsp 和 Servlet 的区别

10. Tomcat 和 Servlet 的联系

11. cookie 和 session

12. JavaEE 中的三层结构和 MVC

13. RESTful 架构

1. 什么是 Servlet?

Servlet 是在服务器上运行的小程序。一个 servlet 就是一个 Java 类,并且可以通过 “请求—响应” 编程模式来访问的这个驻留在服务器内存里的 servlet 程序。

2. Servlet 类的继承关系

3. Servlet 实现方式

  • 实现 javax.servlet.Servlet 接口

  • 继承 javax.servlet.GenericServlet 类

  • 继承 javax.servlet.http.HttpServlet 类

    通常会去继承 HttpServlet 类来完成 Servlet

4. Tomcat 容器等级

Tomcat 的容器分为4个等级,Servlet 的容器管理 Context 容器,一个 Context 对应一个 Web 工程。

5. Servlet 执行流程

主要描述了从浏览器到服务器,再从服务器到浏览器的整个执行过程。

1.浏览器请求

浏览器向服务器请求时,服务器不会直接执行我们的类,而是到 web.xml 里寻找路径名。

① 浏览器输入访问路径后,携带了请求行,头,体

② 根据访问路径找到已注册的 servlet 名称

③ 根据映射找到对应的 servlet 名

④ 根据根据 servlet 名找到我们全限定类名,既我们自己写的类

2.服务器创建对象

① 服务器找到全限定类名后,通过反射创建对象,同时也创建了 servletConfig,里面存放了一些初始化信息(注意服务器只会创建一次 servlet 对象,所以 servletConfig 也只有一个)

3.调用 init 方法

① 对象创建好之后,首先要执行 init 方法,但是我们发现我们自定义类下没有 init 方法,所以程序会到其父类 HttpServlet 里找

② 我们发现 HttpServlet 里也没有 init 方法,所以继续向上找,既向其父类 GenericServlet 中继续寻找,在 GenericServlet 中我们发现了 init 方法,则执行 init 方法(对接口 Servlet 中的 init 方法进行了重写)

注意: 在 GenericServlet 中执行 public void init(ServletConfig config) 方法的时候,又调用了自己无参无方法体的 init() 方法,其目的是为了方便开发者,如果开发者在初始化的过程中需要实现一些功能,可以重写此方法。

4.调用 service 方法

接着,服务器会先创建两个对象:ServletRequest 请求对象和 ServletResponse 响应对象,用来封装浏览器的请求数据和封装向浏览器的响应数据

① 接着服务器会默认在我们写的类里寻找 service(ServletRequest req, ServletResponse res) 方法,但是 DemoServlet 中不存在,那么会到其父类中寻找

② 到父类 HttpServlet 中发现有此方法,则直接调用此方法,并将之前创建好的两个对象传入

③ 然后将传入的两个参数强转,并调用 HttpServlet 下的另外个 service 方法

④ 接着执行 service(HttpServletRequest req, HttpServletResponse resp) 方法,在此方法内部进行了判断请求方式,并执行 doGet 和 doPost,但是 doGet 和 doPost 方法已经被我们自己重写了,所以会执行我们重写的方法

看到这里,你或许有疑问:为什么我们不直接重写 service 方法? 因为如果重写 service 方法的话,我们需要将强转,以及一系列的安全保护判断重新写一遍,会存在安全隐患。

4.向浏览器响应

6. Servlet 生命周期

  • 加载和实例化:Servlet 容器负责加载和实例化 Servlet 对象。
  • 初始化void init(ServletConfig servletConfig) Servlet 对象创建之后马上执行的初始化方法,只执行一次。
  • 请求处理void service(ServletRequest servletRequest, ServletResponse servletResponse) 每次处理请求都是在调用这个方法,它会被调用多次
  • 销毁void destroy() 在 Servlet 被销毁之前调用,负责释放 Servlet 对象占用的资源的方法

服务器执行流程

  1. Servlet 类由自己编写,但对象由服务器来创建,并由服务器来调用相应的方法。 
  2. 服务器启动时 ( web.xml 中配置 load-on-startup=1,默认为0 ) 或者第一次请求该 Servlet 时,就会初始化一个 Servlet 对象,也就是会执行初始化方法 init(ServletConfig conf)。
  3. 该 Servlet 对象去处理所有客户端请求,在 service(ServletRequest req,ServletResponse res) 方法中执行。
  4. 最后服务器关闭时,才会销毁这个 Servlet 对象,执行 destroy() 方法。

一些问题

Servlet 何时创建? 答:默认第一次访问 Servlet 时创建该对象(调用 init() 方法)

Servlet何时销毁?答:服务器关闭 Servlet 就销毁了(调用 destroy() 方法)

每次访问必须执行的方法是什么?答:public void service(ServletRequest arg0, ServletResponse arg1)

7. Tomcat 装载 Servlet 的三种情况

  • Servlet 容器启动时自动装载某些 Servlet,实现它只需要在 web.xml 文件中的 <servlet></servlet> 之间添加以下代码:

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

    其中,数字越小表示优先级越高。启动和关闭 Tomcat:优先级高的先启动也先关闭。

  • 客户端首次向某个 Servlet 发送请求。

  • Servlet 类被修改后,Tomcat 容器会重新装载 Servlet。

8. forward 和 redirect

Servlet 中主要有两种实现跳转的方式:forward 与 redirect 方式。

forward 是服务器内部的重定向,服务器直接访问目标地址的 URL,把那个 URL 的响应内容读取过来,而客户端并不知道,因此在客户端浏览器的地址栏中不会显示转向后的地址,还是原来的地址。由于在整个定向的过程中用的是同一个 Request,因此 forward 会将 Request 的信息带到被定向的 JSP 或 Servlet 中使用。

redirect 则是客户端的重定向,是完全的跳转,即客户端浏览器会获取到跳转后的地址,然后重新发送请求,因此浏览器中会显示跳转后的地址。同事,由于这种方式比 forward 方式多了一次网络请求,因此其效率要低于 forward 方式。需要注意的是,客户端的重定向可以通过设置特定的 HTTP 头或改写 JavaScript 脚本实现。

鉴于以上的区别,一般当 forward 方式可以满足需求时,尽可能地使用 forward 方式。但在有些情况下,例如,需要跳转到下一个其他服务器上的资源,则必须使用 redirect 方式。

引申:filter的作用是什么?主要实现什么方法?

filter 使用户可以改变一个 request 并且修改一个 response。filter 不是一个 Servlet,它不能产生一个 response,但它能够在一个 request 到达 Servlet 之前预处理 request,也可以在离开 Servlet 时处理 response。filter 其实是一个 “Servlet Chaining” (Servler 链)。

一个 filter 的作用包括以下几个方面:

  1. 在 Servlet 被调用之前截获
  2. 在 Servlet 被调用之前检查 Servlet Request
  3. 根据需要修改 Request 头和 Request 数据
  4. 根据需要修改 Response 头和 Response 数据
  5. 在 Servlet 被调用之后截获

9. Jsp 和 Servlet 的区别

不同之处在哪?

  • Servlet 在 Java 代码中通过 HttpServletResponse 对象动态输出 HTML 内容
  • JSP 在静态 HTML 内容中嵌入 Java 代码,Java 代码被动态执行后生成 HTML 内容

各自的特点

  • Servlet 能够很好地组织业务逻辑代码,但是在 Java 源文件中通过字符串拼接的方式生成动态 HTML 内容会导致代码维护困难、可读性差
  • JSP 虽然规避了 Servlet 在生成 HTML 内容方面的劣势,但是在 HTML 中混入大量、复杂的业务逻辑同样也是不可取的

通过 MVC 双剑合璧

既然 JSP 和 Servlet 都有自身的适用环境,那么能否扬长避短,让它们发挥各自的优势呢?答案是肯定的——MVC(Model-View-Controller)模式非常适合解决这一问题。

MVC模式(Model-View-Controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller):

  • Controller——负责转发请求,对请求进行处理
  • View——负责界面显示
  • Model——业务功能编写(例如算法实现)、数据库设计以及数据存取操作实现

在 JSP/Servlet 开发的软件系统中,这三个部分的描述如下所示:

  1. Web 浏览器发送 HTTP 请求到服务端,被 Controller(Servlet) 获取并进行处理(例如参数解析、请求转发)
  2. Controller(Servlet) 调用核心业务逻辑——Model部分,获得结果
  3. Controller(Servlet) 将逻辑处理结果交给 View(JSP),动态输出 HTML 内容
  4. 动态生成的 HTML 内容返回到浏览器显示

MVC 模式在 Web 开发中的好处是非常明显,它规避了 JSP 与 Servlet 各自的短板,Servlet 只负责业务逻辑而不会通过 out.append() 动态生成 HTML 代码;JSP 中也不会充斥着大量的业务代码。这大大提高了代码的可读性和可维护性。

10. Tomcat 和 Servlet 的联系

Tomcat 是 Web 应用服务器,是一个 Servlet/JSP 容器。Tomcat 作为 Servlet 容器,负责处理客户请求,把请求传送给 Servlet,并将 Servlet 的响应传送回给客户。而 Servlet 是一种运行在支持 Java 语言的服务器上的组件。Servlet 最常见的用途是扩展 Java Web 服务器功能,提供非常安全的,可移植的,易于使用的 CGI 替代品。

① Tomcat 将 HTTP 请求文本接收并解析,然后封装成 HttpServletRequest 类型的 request 对象,所有的 HTTP 头数据读可以通过 request 对象调用对应的方法查询到。

② Tomcat 同时会要响应的信息封装为 HttpServletResponse 类型的 response 对象,通过设置 response 属性就可以控制要输出到浏览器的内容,然后将 response 交给 tomcat,Tomcat 就会将其变成响应文本的格式发送给浏览器。

Java Servlet API 是 Servlet 容器(tomcat) 和 servlet 之间的接口,它定义了 serlvet 的各种方法,还定义了 Servlet 容器传送给 Servlet 的对象类,其中最重要的就是 ServletRequest 和 ServletResponse。所以说我们在编写 servlet 时,需要实现 Servlet 接口,按照其规范进行操作。

什么是 cookie?

Cookie 是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现 Session 的一种方式。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie 使基于无状态的 HTTP 协议记录稳定的状态信息成为了可能。

什么是 session?

Session 代表着服务器和客户端一次会话的过程。Session 对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当客户端关闭会话,或者 Session 超时失效时会话结束。

二者区别

区别 cookie session
作用范围 保存在客户端(浏览器) 保存在服务器端
存取方式 只能保存 ASCII 可以存任意数据类型,一般情况下我们可以在 Session 中保持一些常用变量信息,比如说 UserId 等
有效期 可设置为长时间保持,比如我们经常使用的默认登录功能 一般失效时间较短,客户端关闭或者 Session 超时都会失效
隐私策略 存储在客户端,比较容易遭到不法获取 存储在服务端,安全性相对 Cookie 要好一些
存储大小 单个 Cookie 保存的数据不能超过 4K 可存储数据远高于 Cookie

说起来为什么需要 Cookie ,这就需要从浏览器开始说起,我们都知道浏览器是没有状态的(HTTP 协议无状态),这意味着浏览器并不知道是张三还是李四在和服务端打交道。这个时候就需要有一个机制来告诉服务端,本次操作用户是否登录,是哪个用户在执行的操作,那这套机制的实现就需要 Cookie 和 Session 的配合。

既然服务端是根据 Cookie 中的信息判断用户是否登录,那么如果浏览器中禁止了 Cookie,如何保障整个机制的正常运转。

第一种方案,每次请求中都携带一个 SessionID 的参数,也可以 Post 的方式提交,也可以在请求的地址后面拼接 xxx?SessionID=123456...

第二种方案,Token 机制。Token 机制多用于 App 客户端和服务器交互的模式,也可以用于 Web 端做用户状态管理。

Token 的意思是“令牌”,是服务端生成的一串字符串,作为客户端进行请求的一个标识。Token 机制和 Cookie 和 Session 的使用机制比较类似。

当用户第一次登录后,服务器根据提交的用户信息生成一个 Token,响应时将 Token 返回给客户端,以后客户端只需带上这个 Token 前来请求数据即可,无需再次登录验证。

如何考虑分布式 Session 问题?

在互联网公司为了可以支撑更大的流量,后端往往需要多台服务器共同来支撑前端用户请求,那如果用户在 A 服务器登录了,第二次请求跑到服务 B 就会出现登录失效问题。

分布式 Session 一般会有以下几种解决方案:

  • Nginx ip_hash 策略,服务端使用 Nginx 代理,每个请求按访问 IP 的 hash 分配,这样来自同一 IP 固定访问一个后台服务器,避免了在服务器 A 创建 Session,第二次分发到服务器 B 的现象。
  • Session 复制,任何一个服务器上的 Session 发生改变(增删改),该节点会把这个 Session 的所有内容序列化,然后广播给所有其它节点。
  • 共享 Session,服务端无状态话,将用户的 Session 等信息使用缓存中间件来统一管理,保障分发到每一个服务器的响应结果都一致。

12. JavaEE 中的三层结构和 MVC

做企业应用开发时,经常采用三层架构分层:表示层、业务层、持久层。

表示层:负责接收用户请求、转发请求、显示数据等。

业务层:负责组织业务逻辑。

持久层:负责持久化业务对象。

这三个分层,每一层都有不同的模式,就是架构模式。表示层最常用的架构模式就是 MVC。

MVC 是客户端的一种设计模式,所以他天然就不考虑数据如何存储的问题。作为客户端,只需要解决用户界面、交互和业务逻辑就好了。在 MVC 模式中,View 负责的是用户界面,Controller 负责交互,Model 负责业务逻辑。至于数据如何存储和读取,当然是由 Model 调用服务端的接口来完成。

在三层架构中,并没有客户端/服务端的概念,所以表示层、业务层的任务其实和 MVC 没什么区别,而持久层在 MVC 里面是没有的。

总结:MVC = 表示层 + 业务层,但不包括持久层。

13. RESTful 架构

什么是REST?

REST 是所有 Web 应用都应该遵守的架构设计指导原则。 面向资源是 REST 最明显的特征,对于同一个资源的一组不同的操作。对于每个资源只能执行一组有限的操作。(7个HTTP方法:GET/POST/PUT/DELETE/PATCH/HEAD/OPTIONS)

常用操作

HTTP方法 功能
GET select,从服务器取出资源(一项或多项)。
POST create,在服务器新建一个资源。
PUT update,在服务器更新资源(客户端提供改变后的完整资源)。
DELETE delete,从服务器删除资源。