1、什么是 Servlet


  • Servlet 是运行在服务器上的一个 java 小程序,它可以接收客户端发送过来的请求,并响应数据给客户端;
  • Servlet 是 JavaEE 规范之一,规范就是接口;
  • Servlet 就 JavaWeb 三大组件之一。三大组件分别是:Servlet 程序、Filter 过滤器、Listener 监听器。

Servlet(Server Applet)是 Java Servlet 的简称,是使用 Java 语言编写的运行在服务器端的程序。具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。

通常来说,Servlet 是指所有实现了 Servlet 接口的类:

  • Servlet 主要用于处理客户端传来的 HTTP 请求,并返回一个响应,它能够处理的请求有 doGet() 和 doPost() 等;
  • Servlet 由 Servlet 容器提供,Servlet 容器是指提供了 Servlet 功能的服务器(如 Tomcat);
  • Servlet 容器会将 Servlet 动态加载到服务器上,然后通过 HTTP 请求和 HTTP 应与客户端进行交互;

Servlet 应用程序的体系结构如图:

在这里插入图片描述

在图中,Servlet 的请求首先会被 HTTP 服务器(如 Apache)接收,HTTP 服务器只负责静态 HTML 页面的解析,而 Servlet 的请求会转交给 Servlet 容器,Servlet 容器会根据 web.xml 文件中的映射关系,调用相应的 Servlet,Servlet 再将处理的结果返回给 Servlet 容器,并通过 HTTP 服务器将响应传输给客户端。


2、Servlet 的生命周期


Servlet 生命周期分为五个阶段:

  • 1、加载:容器通过类加载器使用 Servlet 类对应的文件来加载 Servlet;
  • 2、创建:通过调用Servlet的构造函数来创建一个Servlet实例;
  • 3、初始化:通过调用Servlet的init()方法来完成初始化工作,这个方法是在Servlet已经被创建,但在向客户端提供服务之前调用;
  • 4、处理客户请求:Servlet 创建后就可以处理请求,当有新的客户端请求时,Web容器都会创建一个新的线程来处理该请求,接着调用 Servlet 的 Service() 方法来响应客户端请求;
  • 5、卸载:容器在卸载 Servlet 之前需要调用 destroy() 方法,让 Servlet 释放其占用的资源。

初始化阶段:

Servlet 容器加载 Servlet,加载完成后,Servlet 容器会创建一个 Servlet 实例并调用 init() 方法,init() 方法只会调用一次。

Servlet 容器会在以下几种情况装载 Servlet:

  • 在 Servlet 容器启动后,客户首次向 Servlet 发送请求
  • Servlet 类文件被更新后,重新装载

处理客户端请求阶段:

每收到一个客户端请求,服务器就会产生一个新的线程去处理。对于用户的Servlet请求,Servlet 容器会创建一个相对于请求的 ServletRequestServletResponse 对象,并根据请求的不同而调用不同的 doxx() 方法,下面是一个 service 处理方法实例:

public class HelloServlet implements Servlet {
    /**
    * service 方法是专门用来处理请求和响应的
    * @param servletRequest
    * @param servletResponse
    * @throws ServletException
    * @throws IOException
    */
    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        System.out.println("service === Hello Servlet  被访问了");
        // 类型转换(因为它有 getMethod() 方法)
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        // 获取请求的方式
        String method = httpServletRequest.getMethod();
        if ("GET".equals(method)) {
        	doGet();
        } else if ("POST".equals(method)) {
        	doPost();
        }
    }
    /**
    * 做 get 请求的操作
    */
    public void doGet(){
        System.out.println("get  请求");
        System.out.println("get  请求");
    }
    /**
    * 做 post 请求的操作
    */
    public void doPost(){
        System.out.println("post  请求");
        System.out.println("post  请求");
    }
}

终止阶段:

当 web 应用被终止,或者 Servlet 容器终止运行,或者 Servlet 重新装载 Servlet 新实例时,Servlet 容器会调用 Servlet 的 destroy() 方法。


3、Servlet 的工作原理


客户发送一个请求,Servlet 调用 service() 方法对请求进行响应,service() 方法会对请求的方法进行匹配,进入相应的逻辑层,完成请求的响应。

但是 Servlet 接口和 GenericServlet 接口中没有 doGet(),doPost() 等方法,HttpServlet 中定义了这些,但是返回的都是Error 信息,所以每次定义 Servlet 都要重写这些方法。

Sertvlet 和 GenericServlet 是不特定于任何协议的,而 HttpServlet 是特定于 Http协 议的,所以 HttpServlet 中的 service()方法中将 ServletRequest,ServletResponse 强转为 HttpRequest 和 HttpResponse,最后调用自己的 service 方法去完成响应。


4、转发和重定向的区别


总结:转发是服务器行为,重定向是客户端行为。

转发(Forword):

通过 RequestDispatcher 对象的 forward(HttpServletRequest request,HttpServletResponse response)方法实现的;RequestDispatcher 可以通过 HttpServletRequest 的 getRequestDispatcher() 方法获得。

例如下面的代码就是跳转到 login_success.jsp 页面:

request.getRequestDispatcher("login_success.jsp").forward(request, response);

重定向(Redirect):

重定向是利用服务器返回的状态码来实现的,客户端浏览器请求服务器的时候,服务器会返回一个状态码。服务器通过 HttpServletRequestResponse 的 setStatus(int status) 方法设置状态码。如果服务器返回 301 或者 302,则浏览器会到新的网址重新请求该资源。

常用状态码:

  • 20(OK):表示服务器已经成功接受请求,并将返回客户端所请求的最终结果
  • 301(Moved Permanently):永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替
  • 302(Found):临时移动,与301类似,但资源只是临时被移动,客户端应继续使用原有 URI
  • 400(Bad Request):客户端请求的语法错误,服务器无法理解
  • 403(Forbidden):服务器理解请求客户端的请求,但是拒绝执行此请求
  • 404(Not Found):请求失败,客户端请求的资源没有找到或者是不存在
  • 500(Internal Server Error):服务器内部错误,无法完成请求
  • 502(Bad Gateway):网关错误,作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应
  • 503(Service Unavailable):服务不可用,由于超载或系统维护,服务器暂时的无法处理客户端的请求

从地址栏显示来说

  • forward 是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器。浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址。
  • redirect 是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址,所以地址栏显示的是新的URL。

从数据共享来说

  • forward:转发页面和转发到的页面可以共享request里面的数据
  • redirect:不能共享数据

从运用地方来说

  • forward:一般用于用户登陆的时候,根据角色转发到相应的模块
  • redirect:一般用于用户注销登陆时返回主页面和跳转到其它的网站等

从效率来说

  • forward:效率高
  • redirect:效率低

5、Servlet 与线程安全


Servlet 不是线程安全的,多线程并发的读写会导致数据不同步的问题。 解决的办法是尽量不要定义 name 属性,而是要把 name 变量分别定义在 doGet() 和 doPost() 方法内。虽然使用 synchronized(name){} 语句块可以解决问题,但是会造成线程的等待,不是很科学的办法。

虽然多线程的并发的读写 Servlet 类属性会导致数据不同步,但是如果只是并发地读取属性而不写入,则不存在数据不同步的问题。因此 Servlet 里的只读属性最好定义为 final 类型的。


6、Cookie 和 Session 的区别


我们都知道,Cookie 是存储在客户端的,而 Session 是存储在服务器端的,但它们具体的区别是什么呢?下面我们来具体分析。

Cookie 是什么?

Cookie 是客户端保存用户信息的一种机制,用来记录用户的一些信息。如何识别特定的客户呢?Cookie 就可以做到。每次 HTTP 请求时,客户端都会发送相应的 Cookie 信息到服务端。它的过期时间可以任意设置,如果你不主动清除它,在很长一段时间里面都可以保留着,即便这之间你把电脑关机了。

既然它是存储在客户端的,换句话说通过某些手法我就可以篡改本地存储的信息来欺骗服务端的某些策略,那该怎么办呢?接下来就轮到我们的 Session 出场了。

Session 是什么?

Session是在无状态的HTTP协议下,服务端记录用户状态时用于标识具体用户的机制。它是在服务端保存的用来跟踪用户的状态的数据结构,可以保存在文件、数据库或者集群中。在浏览器关闭后这次的Session就消失了,下次打开就不再拥有这个Session。其实并不是Session消失了,而是Session ID变了,服务器端可能还是存着你上次的Session ID及其Session 信息,只是他们是无主状态,也许一段时间后会被删除。

实际上Cookie与Session都是会话的一种方式。它们的典型使用场景比如“购物车”,当你点击下单按钮时,服务端并不清楚具体用户的具体操作,为了标识并跟踪该用户,了解购物车中有几样物品,服务端通过为该用户创建Cookie/Session来获取这些信息。

如果你的站点是多节点部署,使用Nginx做负载均衡,那么有可能会出现Session丢失的情况(比如,忽然就处于未登录状态)。这时可以使用IP负载均衡(IP绑定 ip_hash,每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决Session的问题),或者将Session信息存储在集群中。在大型的网站中,一般会有专门的Session服务器集群,用来保存用户会话,这时可以使用缓存服务比如Memcached或者Redis之类的来存放Session。

目前大多数的应用都是用 Cookie 实现Session跟踪的。第一次创建Session时,服务端会通过在HTTP协议中反馈到客户端,需要在 Cookie 中记录一个Session ID,以便今后每次请求时都可分辨你是谁。有人问,如果客户端的浏览器禁用了 Cookie 怎么办?建议使用URL重写技术进行会话跟踪,即每次HTTP交互,URL后面都被附加上诸如 sid=xxxxx 的参数,以便服务端依此识别用户。

总结区别如下

  • Cookie 在客户端(浏览器),Session 在服务器端;
  • Cookie的安全性一般,他人可通过分析存放在本地的Cookie并进行Cookie欺骗。在安全性第一的前提下,选择Session更优。重要交互信息比如权限等就要放在Session中,一般的信息记录放Cookie就好了;
  • 单个Cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个Cookie;
  • Session 可以放在 文件、数据库或内存中,比如在使用Node时将Session保存在redis中。由于一定时间内它是保存在服务器上的,当访问增多时,会较大地占用服务器的性能。考虑到减轻服务器性能方面,应当适时使用Cookie;
  • Session 的运行依赖Session ID,而 Session ID 是存在 Cookie 中的,也就是说,如果浏览器禁用了 Cookie,Session 也会失效(但是可以通过其它方式实现,比如在 url 中传递 Session ID);
  • 用户验证这种场合一般会用 Session。因此,维持一个会话的核心就是客户端的唯一标识,即Session ID。
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐