深入了解tomcat中servlet的创建方式

深入了解tomcat中servlet的创建方式

一、 什么是servlet

1.1、用官方的话解释:
Servlet是oracle公司提供的一门用于开发动态web资源的技术,属于javaEE体系中的一种核心规范。
通俗解释一下:就是我们开发人员所编写的一个类,必须直接或者间接实现这个javaEE的核心规范,也就是实现Servlet接口,因为这种类产生的对象可以被浏览器访问到,因此称之为Servlet,并且javaEE中规定了只有Servlet的实现类产生的对象才可以被浏览器访问,就是Servlet.(也就是说这个类要直接或者间接实现了Servlet接口)

二、开始进入servlet的创建

2.1、通过前面介绍,我们知道了一个什么样的类创建的对象可以被浏览器访问,首先我们直接上代码:

package com.briup.web;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class FirstWay implements Servlet {
	public FirstWay() {
		System.out.println("对象创建了");
	}
	@Override
	public void init(ServletConfig config) throws ServletException {
		// TODO Auto-generated method stub
		System.out.println("我是init:我被调用了");
	}
	@Override
	public ServletConfig getServletConfig() {
		// TODO Auto-generated method stub
		return null;
	}
	@Override
	public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
		// TODO Auto-generated method stub
		System.out.println("我是service,我被调用了");	
	}
	@Override
	public String getServletInfo() {
		// TODO Auto-generated method stub
		return null;
	}
	@Override
	public void destroy() {
		// TODO Auto-generated method stub
		System.out.println("我是destory:我被调用了");
	}

}

那么,一个满足servlet的类已经创建好了,接下来抛出疑问

  • servet对象由谁创建?
  • 里面实现的接口方法,哪些会调用,什么时候调用,调用几次?
    第一个疑问: 既然是servlet类,由我们开发人员自己手动创建对象,显然是不合理,所以这个对象的创建,是交给tomcat创建的,我们开发人员只需要告诉 tomcat,让他创建,让他什么时候创建就行了;
    如何告诉?

1、方法一:通过配置webxml的方式。(极其不推荐使用

对于整个动态web项目而言,web.xml是最先加载的配置文件,所以在web.xml的方式配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1">
  <display-name>firstWay</display-name>
  <servlet>
  		<servlet-name>FirstWay</servlet-name>
  		<servlet-class>com.briup.web.FirstWay</servlet-class>
  		<!-- <load-on-startup>1</load-on-startup> -->
  </servlet>
  <servlet-mapping>
  	<servlet-name>FirstWay</servlet-name>
  	<url-pattern>/FirstWay</url-pattern>
  </servlet-mapping>
</web-app>

解释:
1、servlet-name:见名知意:servlet的名字,注意要与下面你设置映射的名字对应
2、serlvet-class:serlvet的全限定名
3、load-on-startup:是否在tomcat启动的时候就创建servlet对象,传入一个大于0的整数‘’(默认是浏览器第一次请求的时候创建servlet对象
4、servlet-mapping:见名知意,设置浏览器的访问映射
5、servlet-name:于上面的对应
6、url-pattern:浏览器的访问映射(假设默认是本机的话,且tomcat的端口号为8080,那么浏览器访问这个servlet的路径为:localhost:8080/项目名/FirstWay
有了这些基础,让我们访问看看;

  • 第一步:启动tomcat
    在这里插入图片描述
    tomcat正常启动
  • 第二步:通过浏览器访问(我们这里手动访问3次)
    在这里插入图片描述
    浏览器访问正常
  • 第三步:观察控制台
    在这里插入图片描述
通过运行结果分析:
  • 第一次启动服务器,对象并没有被创建
  • 浏览器请求三遍,但是对象只创建一次,init()方法也只调用一次
  • 每访问一次,对象便会调用一次service()方法
  • 其他方法没被调用
    解释为嘛没被调用:getServletConfig():得到ServletConfig对象
    : getServletInfo():得到Servlet的信心,比如作者
    :destroy():servlet销毁的时候才会调用这个方法,(比如:tomcati正常关闭 这里我就不去测试,想测试的小伙伴,可以右键service,点击stop)然后再观察控制台便可知了。

2、方法二:注解的方式告诉tomcat(与前者相比,推荐使用

@WebServlet(value ="映射路径")
public Fristservlet implement Servelt {
}

通过这个注解也可以设置,是否在启动服务器的时候就创建对象,这里就不演示了,
注意:(一旦使用了注解的方式告诉tomcat如果创建某个对象,就不能在web.xml里面再对这个servlet进行访问设置了

三、回归主题,servlet的第二种创建方式

有了前面的解释,直接上代码然后再分析

package com.briup.web;

import java.io.IOException;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
@WebServlet(value = "/secondWay")
public class SecondWayCreate extends GenericServlet {

	@Override
	public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
		// TODO Auto-generated method stub
		System.out.println("调用了service方法");
	}

}

  • 1、比第一种方法简洁,实现的是GenericServlet这个类
  • 2、我们看一下GenericServlet源码,然后进行分析;
public abstract class GenericServlet implements Servlet, ServletConfig,

可知,这是个抽线类,是servlet接口的实现类,那么GenericServlet间接 实现了servlet接口,
与第一种方式相比:开发者不是必须将一些接口中不必要的方法实现,可以具有选择性,减少了代码量。然而并没有上面ruan用,就是装b而已

三、重点第三种方式(与前两者相比,我更推荐第三种方式)

直接上代码

package com.briup.web;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(value = "/ThreeWayCreate")
public class ThreeWayCreate extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		super.doGet(req, resp);
	}
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		super.doPost(req, resp);
	}
}

通过以上代码,可能就有小伙伴要问了
不是说servlet要直接或者间接实现servlet接口吗,不是说浏览器每请求一次就要调用一次service方法吗?方法在哪呢?这不是与前面理论冲突了吗?
我们继续看源码,源码才是道理
我在下面值列举源码里面比较核心的部分,需要理解更加深入了解的小伙伴,直接去看源码,tomcat是开源的

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(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        } else {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        }
    }
     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(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
        } else {
            resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
        }
    }

}

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


    /*
     * Sets the Last-Modified entity header field, if it has not
     * already been set and if the value is meaningful.  Called before
     * doGet, to ensure that headers are set before response data is
     * written.  A subclass might have set this header already, so we
     * check.
     */
      public void service(ServletRequest req, ServletResponse res)
        throws ServletException, IOException {

        HttpServletRequest  request;
        HttpServletResponse response;

        try {
            request = (HttpServletRequest) req;
            response = (HttpServletResponse) res;
        } catch (ClassCastException e) {
            throw new ServletException("non-HTTP request or response");
        }
        service(request, response);
    }
}
}

分析:

  • 第一步分析
    在这里插入图片描述
    可知这个抽象类继承了GennericeServlet这个抽象类 也就是逐层往下推,实现了Servle接口,那么这个抽线类必然也继承了serice方法。
  • 第二步分析
    在这里插入图片描述
    这个是继承servlet接口的service方法,当浏览器每请求一次时,都会调用这个方法,由图可知,这个方法已经被HttpServlet实现了,由实现类可以得出,请求对象req,和响应对象res,被强转成了HttpServletRequest,和HttpServletResponse(向下转型),然后将强转的对象,传入HttpServlet重载的Service方法中,调用,第三步,分析重载后的Service(HttpRequest req,HttpRespone res);
  • 第三步分析
  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;
                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)) {
            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);
        }
    }

通过传过来的HttpRequest对象,判断请求方式,通过请求方式,决定调用哪个方法(如果请求方式是post方式,那么就会调用doPost(HttpRequest req,HttpRestpone Res)方法

  • 第四步分析
    综上分析,总结:tomcat创建对象,当浏览器请求的时候,调用Servlet的Service(ServeltRequest req,ServletRespone res )方法,然后这个方法再调用,HttpServlet里面重载的Servlet(HttpServletReqeust req ,HttpServletRespone res)方法,然后这个方法会通过请求方式是什么,选择性的调用doPost(),还是doGet()方法(当然还有很多其他的方式这里就不列举了),
因此第三种方式,的本质还是当浏览器发起一次请求的时候调用了Servlet接口里面的Service(ServeltRequest req,ServletRespone res )方法,然后通过实现类的里面的逻辑,间接的调用了doPost()等方法。

优点:

  • 1、通过请求方式可以处理相应的请求,使得逻辑更加清晰
  • 2,减少代码量,是程序更加简洁
  • 3,使得请求或者响应的操作性更加丰富
  • 4…

四、 总结:

注意点:浏览器发起请求调用的一定是servlet种的service方法;
有任何错误或者见解希望各位读者指出互相进步,相互学习。

  • 24
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 17
    评论
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值