HttpServlet源码分析

本文详细介绍了HttpServlet类在JavaWeb开发中的角色,包括其在jakarta.servlet.http包下的位置,它与GenericServlet的关系,以及Servlet的生命周期过程。特别关注了如何避免doGet/doPost方法导致的405错误,强调了前后端请求方式的一致性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • HttpServlet类是专门为HTTP协议准备的。比GenericServlet更加适合HTTP协议下的开发。

HttpServlet在哪个包下?

   jakarta.servlet.http.HttpServlet

 到目前为止我们接触了servlet规范中哪些接口?

  •    jakarta.servlet.Servlet 核心接口(接口)
  •    jakarta.servlet.ServletConfig Servlet配置信息接口(接口)
  •    jakarta.servlet.ServletContext Servlet上下文接口(接口)
  •    jakarta.servlet.ServletRequest Servlet请求接口(接口)
  •    jakarta.servlet.ServletResponse Servlet响应接口(接口)
  •    jakarta.servlet.ServletException Servlet异常(类)
  •    jakarta.servlet.GenericServlet 标准通用的Servlet类(抽象类)

 http包下都有哪些类和接口呢?jakarta.servlet.http.*;

  •    jakarta.servlet.http.HttpServlet (HTTP协议专用的Servlet类,抽象类)
  •    jakarta.servlet.http.HttpServletRequest (HTTP协议专用的请求对象)
  •    jakarta.servlet.http.HttpServletResponse (HTTP协议专用的响应对象)

 HttpServletRequest对象中封装了什么信息?

  •    HttpServletRequest,简称request对象。
  •    HttpServletRequest中封装了请求协议的全部内容。
  •    Tomcat服务器(WEB服务器)将“请求协议”中的数据全部解析出来,然后将这些数据全部封装到request对象当中了。
  •    也就是说,我们只要面向HttpServletRequest,就可以获取请求协议中的数据。
  •  HttpServletResponse对象是专门用来响应HTTP协议到浏览器的。

 Servlet生命周期:

  •    用户第一次请求
  •      Tomcat服务器通过反射机制,调用无参数构造方法。创建Servlet对象。(web.xml文件中配置的Servlet类对应的对象。)
  •      Tomcat服务器调用Servlet对象的init方法完成初始化。
  •      Tomcat服务器调用Servlet对象的service方法处理请求。
  •    用户第二次请求
  •      Tomcat服务器调用Servlet对象的service方法处理请求。
  •    用户第三次请求
  •      Tomcat服务器调用Servlet对象的service方法处理请求。
  •    ....
  •      Tomcat服务器调用Servlet对象的service方法处理请求。
  •    服务器关闭
  •      Tomcat服务器调用Servlet对象的destroy方法,做销毁之前的准备工作。
  •      Tomcat服务器销毁Servlet对象。
  • public class HelloServlet extends HttpServlet {
    	// 用户第一次请求,创建HelloServlet对象的时候,会执行这个无参数构造方法。
    	public HelloServlet() {
        }
        
        //override 重写 doGet方法
        //override 重写 doPost方法
    }
    
    public abstract class GenericServlet implements Servlet, ServletConfig,
            java.io.Serializable {
               
    	// 用户第一次请求的时候,HelloServlet对象第一次被创建之后,这个init方法会执行。
        public void init(ServletConfig config) throws ServletException {
            this.config = config;
            this.init();
        }
    	// 用户第一次请求的时候,带有参数的init(ServletConfig config)执行之后,会执行这个没有参数的init()
    	public void init() throws ServletException {
            // NOOP by default
        }
    }
    
    // HttpServlet模板类。
    public abstract class HttpServlet extends GenericServlet {
        // 用户发送第一次请求的时候这个service会执行
        // 用户发送第N次请求的时候,这个service方法还是会执行。
        // 用户只要发送一次请求,这个service方法就会执行一次。
        @Override
        public void service(ServletRequest req, ServletResponse res)
            throws ServletException, IOException {
    
            HttpServletRequest  request;
            HttpServletResponse response;
    
            try {
                // 将ServletRequest和ServletResponse向下转型为带有Http的HttpServletRequest和HttpServletResponse
                request = (HttpServletRequest) req;
                response = (HttpServletResponse) res;
            } catch (ClassCastException e) {
                throw new ServletException(lStrings.getString("http.non_http"));
            }
            // 调用重载的service方法。
            service(request, response);
        }
        
        // 这个service方法的两个参数都是带有Http的。
        // 这个service是一个模板方法。
        // 在该方法中定义核心算法骨架,具体的实现步骤延迟到子类中去完成。
        protected void service(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
            // 获取请求方式
            // 这个请求方式最终可能是:""
            // 注意:request.getMethod()方法获取的是请求方式,可能是七种之一:
            // GET POST PUT DELETE HEAD OPTIONS TRACE
            String method = req.getMethod();
    
            // 如果请求方式是GET请求,则执行doGet方法。
            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;
                    try {
                        ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
                    } catch (IllegalArgumentException iae) {
                        // Invalid date header - proceed as if none was set
                        ifModifiedSince = -1;
                    }
                    if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                        // 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)) {
                // 如果请求方式是POST请求,则执行doPost方法。
                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);
            }
        }
        
        
        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException{
            // 报405错误
            String msg = lStrings.getString("http.method_get_not_supported");
            sendMethodNotAllowed(req, resp, msg);
        }
        
        protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
            // 报405错误
            String msg = lStrings.getString("http.method_post_not_supported");
            sendMethodNotAllowed(req, resp, msg);
        }
        
    }
    
    /*
    通过以上源代码分析:
    	假设前端发送的请求是get请求,后端程序员重写的方法是doPost
    	假设前端发送的请求是post请求,后端程序员重写的方法是doGet
    	会发生什么呢?
    		发生405这样的一个错误。
    		405表示前端的错误,发送的请求方式不对。和服务器不一致。不是服务器需要的请求方式。
    	
    	通过以上源代码可以知道:只要HttpServlet类中的doGet方法或doPost方法执行了,必然405.
    
    怎么避免405的错误呢?
    	后端重写了doGet方法,前端一定要发get请求。
    	后端重写了doPost方法,前端一定要发post请求。
    	这样可以避免405错误。
    	
    	这种前端到底需要发什么样的请求,其实应该后端说了算。后端让发什么方式,前端就得发什么方式。
    	
    */

    步骤总结:

    • 第一步:编写一个Servlet类,直接继承HttpServlet
    • 第二步:重写doGet方法或者重写doPost方法,到底重写谁,javaweb程序员

    • 第三步:将Servlet类配置到web.xml文件当中

    • 第四步:准备前端的页面(form表单),form表单中指定请求路径即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值