首页 > 技术文章 > servlet与tomcat

huyangshu-fs 2021-01-10 23:26 原文

1、servlet与servlet容器

 (1)servlet本质

   前方高能,请注意、注意、注意。。。重要的事情说三遍,servlet本质就是一个Java接口 ,目的在于定义一套处理网络请求的规范,如下所示:

package javax.servlet;

import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

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

    ServletConfig getServletConfig();

    void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException;

    String getServletInfo();

    void destroy();
}

 

  所有实现servlet接口的类,都要实现接口中的五个方法,其中最主要的是两个生命周期方法 init()和destroy()、处理请求的service(),即:

  • 初始化的作用;
  • 销毁后的作用;
  • 收到请求后需要做什么。

 init() 和 destroy()方法只执行一次, 即servlet的生命周期---创建和销毁,而service()方法每次有新请求到达时都会调用,即处理处理实际业务。

(2)servlet容器

  全称server applet,意为服务程序。主要作用是给上级容器(Tomcat)提供doGet()和doPost()等方法。其生命周期实例化、初始化、调用、销毁受控于Tomcat容器。

(3)request/response

  request:浏览器发起http请求,请求到达tomcat,经tomcat封装成了request对象(将请求头、请求地址、请求参数等进行了封装);

  response:浏览器发起http请求到达tomcat容器时,产生一个空的response对象,http请求数据经过servlet处理,将处理后结果封装成response对象,经过tomcat处理后,组装成HTTP响应返回给浏览器。

 (4)web容器

  可以部署多个WEB应用程序的环境。Tomcat容器属于web容器的一种,web容器还包括weblogic容器、JBoss容器等;而Tcomcat、webLogic等包含servlet容器。

  

  注:三层架构实质是对servlet的拆分,目的在于解耦合。

 (5)cookie/session

  1)cookie

   1.1)原理

  当在服务器端的代码里写了response.cookie["testCookie"]="testCookieString"的时候,返回给客户端的http响应中,会在http响应头中加入cookie的信息,浏览器收到响应后,会按照http响应头里的cookie在客户端建立cookie。客户端的cookie也是插在http头中发送到服务器端的,并且,一个域(同一个域名),在客户端建立的所有cookie,在客户端的每一次的http请求都会带着,比如testwebsite.com这个站点,任何时刻写到客户端的cookie,只要不过期,浏览器在向testwebsite.com发送http请求的时候,会带上这些cookie,所以cookie在客户端的大小是有限制的。

   1.2)分类

  cookie按照在客户端存放的方式,可以分为两类:

  ①会话性质的cookie,存放在浏览器内存中,当你在用代码向客户端写入cookie的时候,如果没有指定过期时间,那么cookie是存放在浏览器的内存里面的,不会持久化在硬盘上。
  ②持久化的cookie,存放在硬盘上,当指定了cookie的过期时间,则在客户端写入cookie的时候就会在浏览器的临时文件下生成一个文件。

   2)session

  HTTP请求中的会话信息,保存在服务端,通过sessionId关联访问。如果是第一次取session,服务器会创建一个session对象(session本身是一个map集合),并且存入服务器的session集合中以sessionId为标识键,也就是根据sessionId即可取到对应session的引用。同时使用session servlet还获得了一个sessionId,在响应时把这个sessionId以cookie的形式发给了客户端。如果客户访问其它同一域(域名)的servlet,这个sessionId会跟着请求上传到服务器。那么如果请求的另一个servlet也要使用session,会先检查有没有这个保存sessionId的cookie,如果有则直接到session集合中取对应的session引用返回给要使用的servlet,所以,只要客户端存在这个sessionId,不管请求哪个servlet,都可以拿到同一个session。所以session就可以给不同的请求存储数据。

2、tomcat

(1)tomcat程序入口main()

  main方法是启动一个java程序的入口,任何程序都会有自己的main方法,tomcat是一个WEB容器,也不例外:

//TODO 以下为伪代码,目的在于说明tomcat执行原理
public static void main(String args[]) {
       //TODO 初始化运行环境,载入需要的jar包,读取conf/server.xml,生成相应的运行对象:
        if (daemon == null) {
            Bootstrap bootstrap = new Bootstrap();
            try {
                bootstrap.init();//初始化守护进程
            } catch (Throwable t) {
                return;
            }
            daemon = bootstrap;
        } 
   //TODO 装载,开始运行。
        try {
            String command = "start";
            if (command.equals("startd")) {
                daemon.load(args);
                daemon.start();//启动Tomcat
            } else if (command.equals("stopd")) {
                args[args.length - 1] = "stop";
                daemon.stop();//停止Tomcat守护进程
            } 
        } catch (Throwable t) {
        }
    }

(2)tomcat创建的resques和response

 

(3)tomcat对servlet接口的实现

   逻辑层级关系:

    abstract class GenericServlet implements Servlet, ServletConfig,Serializable

    abstract class HttpServlet extends GenericServlet

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(405, msg);
        } else {
            resp.sendError(400, msg);
        }
    }
    
    //TODO doHead doPut doDelete

    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(405, msg);
        } else {
            resp.sendError(400, msg);
        }
    }

    protected void service(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        String method = req.getMethod();
        long errMsg;
        if (method.equals("GET")) {
            errMsg = this.getLastModified(req);
            if (errMsg == -1L) {
                this.doGet(req, resp);
            } else {
                long ifModifiedSince;
                try {
                    ifModifiedSince = req.getDateHeader("If-Modified-Since");
                } catch (IllegalArgumentException arg8) {
                    ifModifiedSince = -1L;
                }

                if (ifModifiedSince < errMsg / 1000L * 1000L) {
                    this.maybeSetLastModified(resp, errMsg);
                    this.doGet(req, resp);
                } else {
                    resp.setStatus(304);
                }
            }
        }  else if (method.equals("POST")) {
            this.doPost(req, resp);
        } else {
            String errMsg1 = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[] { method };
            errMsg1 = MessageFormat.format(errMsg1, errArgs);
            resp.sendError(501, errMsg1);
        }
    }

    public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException {
        HttpServletRequest request;
        HttpServletResponse response;
        try {
            request = (HttpServletRequest) req;
            response = (HttpServletResponse) res;
        } catch (ClassCastException arg5) {
            throw new ServletException(lStrings.getString("http.non_http"));
        }

        this.service(request, response);
    }
}

 

(4)web容器的部署

  部署的过程其实就是解析 xml 实例化对象,并触发和处理容器及组件对应生命周期事件的过程。在 Tomcat 中,一个 Context 实例就代表一个 Web 应用程序,所以部署的第一步就是创建一个 StandardContext 对象。在创建时 HostConfig 首先会查找 Context 描述符,它可能在两个位置:

  • $CATALINA_BASE/conf/<engine>/<host>/[webappname].xml
  • $CATALINA_BASE/webapps/webappname/META_INF/context.xml

  如果两个位置都不存在此文件,则使用 conf/context.xml 默认配置。

  Context 实例化后会触发 init 和 start 生命周期事件:

  • init - 会创建用于解析 context.xml 和 web.xml 的工具 Digester 的实例,并解析context.xml
  • start - 则会根据 web.xml 部署描述符实例化 Servlet、Filter、Listener 等 Web 组件

 

3、重定向与请求转发

  (1)定义及区别

   重定向:

   1.可以理解为是客户端行为,客户端发起一次请求,服务器端给出一次响应,但这个响应包含下一次客户端需要访问的服务器端处理程序的地址,客户端再次发起请求,将会得到处理结果,也就意味着重定向客户端至少发起两次请求。

      2.当使用了重定向跳转页面后,在其客户端路径栏显示的是重定向的路径。

   3.重定向可以跨域访问,而转发是在web服务器内部进行的,不能跨域访问。

  请求转发:

    1.可以理解是服务器端行为,客户端发起一次请求,在整个服务器端可以被多次传递,但都是由服务器端的处理程序传递给另一个处理程序,无论这个请求经历过多少个处理程序,始终都是同一个请求。

    2.可共享request域(request.setAttribute(), request.getAttribute())中的数据,重定向获取不到request域中的数据,只能用session。

    3.forward()相对高效,在可以满足需要时,尽量使用RequestDispatcher.forward()方法。

 (2)核心原理分析

  HTTP协议规定了一种重定向机制,重定向的运作流程如下:

  1)用户在浏览器端输入特定URL,请求访问服务器端的某个组件。
  2)服务器端的组件返回一个状态码为302的响应结果,该响应结果的含义为:让浏览器端再请求访问另一个Web组件,在响应结果中提供了另一个Web组件的URL。另一个Web组件有可能在同一个Web服务器上,也有可能不再同一个Web服务器上。
  3)当浏览器端接收到这种响应结果后,再立即自动请求访问另一个Web组件。
  4)浏览器端接收到另一个Web组件的响应结果。

  源组件和目标组件不共享同一个ServletRequest对象,因此不共享请求范围内的共享数据。如果以“/”开头,表示相对于当前服务器根路径的URL,如果以http://开头,表示一个完整的URL。

 

  感谢阅读,参考了不少大佬资料,如需转载,请注明出处 https://www.cnblogs.com/huyangshu-fs/p/13173474.html

推荐阅读