Servlet的继承结构和执行原理

 我们在上一篇里,创建了一个servlet程序,并且用浏览器进行了访问,发现根据form标签中method里面的get和post可以分别调到Servlet中的doGet方法和doPost方法,那么是怎么就调到了这两个方法呢。


Servlet的继承结构和执行原理

我们在上一篇里还提到了一下Serlect的继承结构

Servlet  (顶层接口)---->GenericServlet  (抽象类) ----->HttpServlet(抽象类)----->我们自定义的Servlet

我们来剖析一下里面所包含的内容:

 

我们来观察一下在上一篇创建的servlet程序:

package com.yht.servlet;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyFirstServlet extends HttpServlet {

	protected void doGet(HttpServletRequest request,HttpServletResponse response) throws IOException,ServletException{
		System.out.println("doGet方法执行了。。。");
	}
	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("doPost方法执行了。。。");
	}
}

我们可以发现 我们自定义的类需要继承HttpServlet,而且这些类导入的包前面是javax,而这个是在我们自带的jdk中找不到的。

而这些是在我们的tomcat的安装目录里存储的:

我们把上面的jar包复制一份出来,然后进行解压  可以看到里面的目录:

然后我们在Servlet文件夹下发现了 Servlet.class文件

又在子目录http里找到了相应的其他class文件。


我们对上面的class文件进行反编译:

Servlet  :

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 var1) throws ServletException;

   ServletConfig getServletConfig();

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

   String getServletInfo();

   void destroy();
}

可以看出里面一共定义了5个方法。而对于我们最重要的就是 service方法,这里先记住,等到下面再进行讲解:

GenericServlet :

package javax.servlet;

import java.io.IOException;
import java.io.Serializable;
import java.util.Enumeration;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {

   private static final long serialVersionUID = 1L;
   private transient ServletConfig config;


   public void destroy() {
   }

   public String getInitParameter(String name) {
      return this.getServletConfig().getInitParameter(name);
   }

   public Enumeration getInitParameterNames() {
      return this.getServletConfig().getInitParameterNames();
   }

   public ServletConfig getServletConfig() {
      return this.config;
   }

   public ServletContext getServletContext() {
      return this.getServletConfig().getServletContext();
   }

   public String getServletInfo() {
      return "";
   }

   public void init(ServletConfig config) throws ServletException {
      this.config = config;
      this.init();
   }

   public void init() throws ServletException {
   }

   public void log(String msg) {
      this.getServletContext().log(this.getServletName() + ": " + msg);
   }

   public void log(String message, Throwable t) {
      this.getServletContext().log(this.getServletName() + ": " + message, t);
   }

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

   public String getServletName() {
      return this.config.getServletName();
   }
}

这是一个抽象类,实现了Servlet的所有方法,然后又自己添加了属于自己的一些方法,其实这个类在这里只是一个中转的作用,以防万一以后哪一天http协议使用的不广泛了,又出现了更实用的协议,那么可以再创建一个类来继承这个类,而现在还有可能未来很长远的一段时间里还是http协议主导着网络。

HttpServlet: (请看注释:)

package javax.servlet.http;

import java.io.IOException;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Enumeration;
import java.util.ResourceBundle;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.NoBodyResponse;

public abstract class HttpServlet extends GenericServlet {


	/*
	这下面的都是定义的成员变量,表示着所以html所发出的请求的种类
	在html端,发回的请求不只有 get  和 post 两种
	但是这两种是最最常用的,所以其他的现在只了解一下即可
	
	*/
   private static final long serialVersionUID = 1L;
   private static final String METHOD_DELETE = "DELETE";
   private static final String METHOD_HEAD = "HEAD";
   private static final String METHOD_GET = "GET";
   private static final String METHOD_OPTIONS = "OPTIONS";
   private static final String METHOD_POST = "POST";
   private static final String METHOD_PUT = "PUT";
   private static final String METHOD_TRACE = "TRACE";
   private static final String HEADER_IFMODSINCE = "If-Modified-Since";
   private static final String HEADER_LASTMOD = "Last-Modified";
   private static final String LSTRING_FILE = "javax.servlet.http.LocalStrings";
   private static ResourceBundle lStrings = ResourceBundle.getBundle("javax.servlet.http.LocalStrings");

	//当前端发来 get请求的时候,就会调用这个方法,我们自定义的servlet就是大部分情况下重写了这个方法
	//如果没有进行重写,但是浏览器端发来get请求的时候,既然自定义的servlet类中没有重写,那么就会到父类
	//中找到这个方法,而下面的这个方法以及下下面的post方法的作用就是抛出一个错误页面返回到浏览器端
	//错误代码是  405,含义就是服务器端不能够响应 浏览器端的请求。
	//当我们进行重写的时候,就可以进行数据的处理,而不会调用父类里的这个方法

   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);
      }

   }
	
	//doPost的性质和doget对于服务器接受请求这个方面一模一样,也是不重写的时候就会报 405错误。  两者区别会再详细介绍

   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);
      }

   }
   
   
   //...................这里有我删除的大部分对于其他请求的方法,因为暂时以及很长时间内都不用了解,所以先删除了,避免占据篇幅
   
   

	//最最重要的一个service方法,他的作用就是进行相应分流,所有的请求进入服务端都会首先进入service方法,然后再根据请求的种类
	//再去调用针对各种请求的方法,因为首先会进入service方法,所以如果我们自定义的servlet重写这个方法的话,那么就会
	//首先调用重写的方法,其实在重写的service方法内就可以完成各种需求,他可以代替其他的所有方法来独自进行所有功能的实现,
	//但是,因为请求的种类众多,为了让功能具体化,
	//所以就采用service对请求进行分流,各做各的事情,互不打扰。也提高了代码的复用率,所以一般不进行重写。
   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 var9) {
               ifModifiedSince = -1L;
            }

            if(ifModifiedSince < errMsg / 1000L * 1000L) {
               this.maybeSetLastModified(resp, errMsg);
               this.doGet(req, resp);
            } else {
               resp.setStatus(304);
            }
         }
      } else if(method.equals("HEAD")) {
         errMsg = this.getLastModified(req);
         this.maybeSetLastModified(resp, errMsg);
         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 errMsg1 = lStrings.getString("http.method_not_implemented");
         Object[] errArgs = new Object[]{method};
         errMsg1 = MessageFormat.format(errMsg1, errArgs);
         resp.sendError(501, errMsg1);
      }

   }

   
	//这里是对serviece方法的一个重载
   public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
      HttpServletRequest request;
      HttpServletResponse response;
      try {
         request = (HttpServletRequest)req;
         response = (HttpServletResponse)res;
      } catch (ClassCastException var6) {
         throw new ServletException("non-HTTP request or response");
      }

      this.service((HttpServletRequest)request, (HttpServletResponse)response);
   }

}

因为实在太多,所以进行了删减,详情请看上面注释。


所以现在我们有了一个框架:

所以基本的流程就是:

浏览器发来需求,然后进入HttpServlet中进行分流,针对不同的请求来执行相应的方法,自定义servlet如果重写doget方法和dopost方法,那么就执行重写的方法,如果找不到自定义servlet重写的方法,就会返回一个错误代码为405的页面,如果自定义servlet代码重写了service,那么就根据重写的service方法执行里面的语句。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值