Servlet源码笔记

主要简单介绍下servlet源码结构

介绍

首先类的主要结构关系需要提及一下

模块一
interface ServletRequest

interface HttpServletRequest extends ServletRequest

class ServletRequestWrapper implements ServletRequest

class HttpServletRequestWrapper extends ServletRequestWrapper implements HttpServletRequest

模块二
interface ServletConfig

interface Servlet

abstract class GenericServlet implements Servlet, ServletConfig, java.io.Serializable

abstract class HttpServlet extends GenericServlet

看到上面的类继承关系可能会有点陌生,接下来我给出一段demo

public class HelloWord extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response){
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response){
    }

好了,这下应该不陌生了,我们用Servlet写代码一般都是继承HttpServlet来进行,这属于第二模块,而我们代码中操作的request属于第一模块

在具体分析代码之前,有必要先科普下Servlet的生命周期,平时我们在写Servlet服务端的时候,是没有main入口类的,仔细想想,没有入口类为何可以启动?
答案来了,是因为tomcat,如果将Servlet看作对象的话,那么tomcat就是Servlet的容器,tomcat负责操控Servlet的生命周期,tomcat从他自己的入口类启动,运行时调用Servlet从而进行一切操作。
我看了很多的博客教程,他们都是这么说的:

 tomcat作为servlet容器,当http请求进来时,发现没有servlet,那么则初始化一个servlet,将http请求封装为Request交给servlet处理,且servlet为单例重复使用,若长时间未调用才会销毁

但是在tomcat8.5.28+servlet4.0环境下,在不调整任何参数时(默认),我的测试跟上述操作有点出入,servlet并不是在接到http请求时才初始化,而是在随tomcat启动时便已经初始化,这一点可以根据我对servlet初始化init方法打断点,并且以debug方式启动可以看出,各位尽可以自行尝试,当然这不是重点,大体流程了解即可。

首先我们从第二模块开始
  • Servlet这个接口类定义了一系列与tomcat相互交互的一系列接口
  • ServletConfig看名字也知道是提供配置信息的一个接口
  • GenericServlet这个是对Servlet和ServletConfig接口的一些实现,另外增加了一些log方法来传递异常
  • HttpServlet这个类就定义了对GET,PUT,POST,HEAD,DELETE等各种HTTP方法的处理方式
    那么问题来了,我们知道之前定义的Servlet接口类提供给tomcat一些交互接口,那么唯一涉及到各种操作的只有service方法,他是如何跟各种HTTP方法的处理结合起来的呢?
    在源码面前的朋友可以追着service方法一路下来,最终在HttpServlet中可以看到service方法的实现
protected void service(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException
    {
        String method = req.getMethod();

        if (method.equals(METHOD_GET)) {
            long lastModified = getLastModified(req);
            if (lastModified == -1) {
                // servlet doesn't support if-modified-since, no reason
                // to go through further expensive logic
                doGet(req, resp);
            } else {
                long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                if (ifModifiedSince < lastModified) {
                    // If the servlet mod time is later, call doGet()
                    // Round down to the nearest second for a proper compare
                    // A ifModifiedSince of -1 will always be less
                    maybeSetLastModified(resp, lastModified);
                    doGet(req, resp);
                } else {
                    resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                }
            }

        } else if (method.equals(METHOD_HEAD)) {
            long lastModified = getLastModified(req);
            maybeSetLastModified(resp, lastModified);
            doHead(req, resp);

        } else if (method.equals(METHOD_POST)) {
            doPost(req, resp);
            
        } else if (method.equals(METHOD_PUT)) {
            doPut(req, resp);
            
        } else if (method.equals(METHOD_DELETE)) {
            doDelete(req, resp);
            
        } else if (method.equals(METHOD_OPTIONS)) {
            doOptions(req,resp);
            
        } else if (method.equals(METHOD_TRACE)) {
            doTrace(req,resp);
            
        } else {
            //
            // Note that this means NO servlet supports whatever
            // method was requested, anywhere on this server.
            //

            String errMsg = lStrings.getString("http.method_not_implemented");
            Object[] errArgs = new Object[1];
            errArgs[0] = method;
            errMsg = MessageFormat.format(errMsg, errArgs);
            
            resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
        }
    }

从代码中可以看到,他是负责匹配头部信息来进行分发操作,不清楚http报文的朋友可以看下面,这是用firefox浏览器发送的一组请求,第一行的GET即为method.equals(…)中的method内容

GET / HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Pragma: no-cache
Cache-Control: no-cache

到此为止第二模块基本结束

接下来分析第一模块
  • ServletRequest主要用来获取被储存信息,例如储存被tomcat封装后的http信息
  • HttpServletRequest特别针对http协议的各种参数在ServletRequest基础上进行了扩展
  • ServletRequestWrapper也是个扩展,不过有个特殊的地方要注意,这个类的构造方法public ServletRequestWrapper(ServletRequest request)接收了一个ServletRequest对象,以后的参数就从这个对象里面拿取
  • HttpServletRequestWrapper就是上面三个类的实现了,没什么意思

#####通过这些介绍,Servlet已经不再神秘,大家可以仔细去看源码,其实Servlet构造十分简单,真正起到关键作用的还是例如Tomcat等Servlet容器
基础知识:{% post_link Tomcat源码笔记 Tomcat源码笔记 %}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值