• (Servlet【九】)HttpServlet类继承GenericServlet类,HttpServlet类源码分析,模板方法设计模式的认识


    一、HttpServlet :

    1. HttpServlet类是专门为HTTP协议准备的。比GenericServlet更适合HTTP协议下的开发。
    2. 位于jakarta.servlet.http.HttpServlet包下。
    3. http包下有哪些接口:
    • jakarta.servlet.http.HttpServlet(HTTP协议专用的Servlet类,抽象类)
    • jakarta.servlet.http.HttpServletRequest(HTTP协议专用的请求对象),封装了HTTP请求协议的全部内容。
    • jakarta.servlet.http.HttpServletResponse(Http协议专用的响应对象),专门用来响应HTTP协议到浏览器的。

    二、HttpServlet 源码分析:

    这是HttpServlet的源码:

    //HttpServlet是一个典型的模板类
    public abstract class HttpServlet extends GenericServlet{
    	//这个service方法是实现的GenericServlet抽象类的抽象方法
        //用户发送第一次请求的时候这个service会执行
        //用户发送第N次请求的时候,这个service方法还是会执行。
        //用户只要发送一次请求,这个service方法就会执行一次。
         public void service(ServletRequest req, ServletResponse res)
             throws ServletException, IOException {
            HttpServletRequest request;
            HttpServletResponse response;
            try {
                //将ServletRequest和ServeltResponse向下转型为带有Http的HttpServletRequest和HttpServletResponse
                request = (HttpServletRequest)req;
                response = (HttpServletResponse)res;
            } catch (ClassCastException var6) {
                throw new ServletException(lStrings.getString("http.non_http"));
            }
    		//调用重载的service方法
            this.service(request, response);
        }
        
        //这个service方法的两个参数都是带有Http的
        //这个service是一个模板方法
        //在该方法中定义核心算法骨架,具体的实现步骤延迟到子类中去完成。。
        protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            //获取请求方式
            //这个请求方式最终可能是:""
            //注意:request.getMethod()方法获取的是请求方式,可能是七种之一:
            //GET POST DELETE HEAD OPTIONS OOPTIONS TRACE
            String method = req.getMethod();
            long lastModified;
            if (method.equals("GET")) {
                lastModified = this.getLastModified(req);
                if (lastModified == -1L) {
                    this.doGet(req, resp);
                } else {
                    long ifModifiedSince;
                    try {
                        ifModifiedSince = req.getDateHeader("If-Modified-Since");
                    } catch (IllegalArgumentException var9) {
                        ifModifiedSince = -1L;
                    }
    
                    if (ifModifiedSince < lastModified / 1000L * 1000L) {
                        this.maybeSetLastModified(resp, lastModified);
                        this.doGet(req, resp);
                    } else {
                        resp.setStatus(304);
                    }
                }
            } else if (method.equals("HEAD")) {
                lastModified = this.getLastModified(req);
                this.maybeSetLastModified(resp, lastModified);
                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 errMsg = lStrings.getString("http.method_not_implemented");
                Object[] errArgs = new Object[]{method};
                errMsg = MessageFormat.format(errMsg, errArgs);
                resp.sendError(501, errMsg);
            }
    
        }
    }
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //报405错误
            String msg = lStrings.getString("http.method_get_not_supported");
            this.sendMethodNotAllowed(req, resp, msg);
        }
    
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            String msg = lStrings.getString("http.method_post_not_supported");
            this.sendMethodNotAllowed(req, resp, msg);
        }
    
    /*
    通过以上源代码分析:
    	假设前端发送的请求是get请求,后端程序员没有重写doGet方法
    	假设前端发送的请求是Post请求,后端程序员重写的方法是doGet
    	会发生什么呢?
    	发生405这样的一个错误,因为后端程序员写的子类实现继承HttpServlet后没有重写doGet方法,导致前端发送get请求后,该类执行service方法,service方法体中的request.getMethod方法识别到发送的是get方法,所以通过if语句算法会去执行doGet方法,但是后端程序员没有重写doGet方法,所以系统会执行父类的doGet方法,父类doGet方法的内容是报405错误。
    	405表示前端你的错误,发送的请求方式不对。和服务器不一致。不是服务器需要的请求方式。
    	通过以上源代码可以知道:只要HttpServlet类中的doGet方法或doPost方法执行了,必然405.
    	怎么避免405错误呢?
    	后端重写doGet方法,前端一定要发get请求。
    	后端重写doPost方法,前端一定要发Post请求。 
    有的人,你会看到为了避免405错误,在Servlet类当中,将doGet和doPost方法都进行了重写。这样,确实可以避免405的放生,但是不建议,405错误还是有用的,该报错的时候应该让他报错。
    */
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98

    三、从中学到的开发技巧:模板设计方法

    技巧一:

    HttpServlet类继承了GenericServlet抽象类,必须实现抽象类的service(ServletRequest,ServletResponse)方法,并在方法内写了很多必要的代码,大大简化了我们的编程。为了防止我们的类在继承HttpServlet类的时候重载他的service()方法,他很巧妙地写了一个空的service(HttpServletRequest,HttpServletResponse)方法,然后他在service(ServletRequest,ServletResponse)方法内调用了service(HttpServletRequest,HttpServletResponse)方法,这样service(ServletRequest,ServletResponse)方法执行的时候service(HttpServletRequest,HttpServletResponse)方法也会执行。这样我们的类重写service(HttpServletRequest,HttpServletResponse)方法既可以保证我们的代码可以在service(ServletRequest,ServletResponse)方法中运行,又可以保证service(ServletRequest,ServletResponse)中原来的代码不被覆盖。

    技巧二:

    同理,HttpServlet类在空的service(HttpServletRequest,HttpServletResponse)方法中加入了一些有用的代码,大大简化了我们的编程。但是也留了入口供我们使用,他又创建了doGet、doPost等空方法,并在service(HttpServletRequest,HttpServletResponse)方法中调用了这些方法,所以以后我们的类继承了HttpServlet类只需要重写doGet、doPost这些方法就可以了。更巧妙的是:为了保证我们重写了这些方法,HttpServlet类又在doGet、doPost等空方法中写了相关代码,代码内容如果执行就会报错。以后执行service()方法的时候必然会调用doGet、doPost等方法,如果我们不重写doGet、doPost这些方法,那会怎么样?根据继承关系,他会调用父类即HttpServlet类的doGet、doPost方法,一单调用就会报错,提示我们要重写这些方法!

    这就是模板方法设计模式。

  • 相关阅读:
    关于XML配置文件中的association标签的使用问题
    Java 版本任你发,我用Java8.(Java 15 新功能介绍 )
    论文:OIE@OIA: an Adaptable and Efficient Open Information Extraction Framework
    机器学习笔记之线性回归
    Spring Security-全面详解(学习总结---从入门到深化)
    ASP.NET Core 6.0 添加 JWT 认证和授权
    css实现进度条
    NSS [鹤城杯 2021]EasyP
    Python进阶系列 - 17讲 函数
    基于 StarRocks 的风控实时特征探索和实践
  • 原文地址:https://blog.csdn.net/m0_53881899/article/details/126388975