servlet详解一:servlet基本介绍

很长时间没接触servlet了,重新认识一下,下面我们需要研究一下几个问题,由于内容较长,几个问题将分为几篇文章详解
1、什么是servle
2、servlet的生命周期
3、servle的继承结构
4、servlet中的几个对象详解
5、HttpServlResponse和HttpServlRequest详解

什么是servlet

Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,主要功能在于交互式地浏览和修改数据,生成动态Web内容。
狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。 —–百度百科

CGI与Servlet的区别和联系

Servlet的程序用来处理请求和发送响应,并且Servlet是为了解决实现动态页面而衍生的东西。 浏览器通过网址来访问服务器,服务器接受请求交给servlet进行处理,然后进行响应。

《servlet详解一:servlet基本介绍》

tomcat和Servlet的关系

Tomcat 是Web应用服务器,是一个Servlet/JSP容器. Tomcat 作为Servlet容器,负责处理客户请求,把请求传送给Servlet,并将Servlet的响应传送回给客户.而Servlet是一种运行在支持Java语言的服务器上的组件. Servlet最常见的用途是扩展Java Web服务器功能,提供非常安全的,可移植的,易于使用的CGI替代品.

从http协议中的请求和响应可以得知,浏览器发出的请求是一个请求文本,而浏览器接收到的也应该是一个响应文本。
《servlet详解一:servlet基本介绍》

①:Tomcat将http请求文本接收并解析,然后封装成ServletRequest(在HttpServlet类中强转为HttpServletRequest)类型的request对象,所有的HTTP头数据读可以通过request对象调用对应的方法查询到。

②:Tomcat同时会要响应的信息封装为ServletResponse(在HttpServlet类中强转为HttpServletRresponse)类型的response对象,通过设置response属性就可以控制要输出到浏览器的内容,然后将response交给tomcat,tomcat就会将其变成响应文本的格式发送给浏览器

Java Servlet API 是Servlet容器(tomcat)和servlet之间的接口,它定义了serlvet的各种方法,还定义了Servlet容器传送给Servlet的对象类,其中最重要的就是ServletRequest和ServletResponse。所以说我们在编写servlet时,需要实现Servlet接口,按照其规范进行操作。

servlet的生命周期

servlet生命周期如下:
Servlet 加载—>实例化—>服务—>销毁。
装载和实例化为一个整体,同时进行
《servlet详解一:servlet基本介绍》
init():

    在Servlet的生命周期中,仅执行一次init()方法。它是在服务器装入Servlet时执行的,负责初始化Servlet对象。可以配置服务器,以在启动服务器或客户机首次访问Servlet时装入Servlet。无论有多少客户机访问Servlet,都不会重复执行init()。

service():

    它是Servlet的核心,负责响应客户的请求。每当一个客户请求一个HttpServlet对象,该对象的Service()方法就要调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。在HttpServlet中已存在Service()方法。默认的服务功能是调用与HTTP请求的方法相应的do功能。

destroy():

    仅执行一次,在服务器端停止且卸载Servlet时执行该方法。当Servlet对象退出生命周期时,负责释放占用的资源。一个Servlet在运行service()方法时可能会产生其他的线程,因此需要确认在调用destroy()方法时,这些线程已经终止或完成。

创建Servlet对象的时机:

Servlet容器启动时:读取web.xml配置文件中的信息,构造指定的Servlet对象,创建ServletConfig对象,同时将ServletConfig对象作为参数来调用Servlet对象的init方法。 在Servlet容器启动后:客户首次向Servlet发出请求,Servlet容器会判断内存中是否存在指定的Servlet对象,如果没有则创建它,然后根据客户的请求创建HttpRequest、HttpResponse对象,从而调用Servlet对象的service方法。

Servlet容器在启动时自动创建Servlet,这是由在web.xml文件中为Servlet设置的属性决定的。从中我们也能看到同一个类型的Servlet对象在Servlet容器中以单例的形式存在。

这种方式必须在web.xml中描述:

    <servlet>
    ...
        <load-on-startup>
            number
        </load-on-startup>
    </servlet>

number<0: 采用延迟装载,只有在第一次请求的容器才在该servlet调用初始化函数

number>=0: 采用预先装载,容器在应用启动时就加载并初始化这个servlet

number越小越先被装载,number越大越晚被装载

servlet的继承结构

servlet的继承结构如下:
《servlet详解一:servlet基本介绍》

1、为什么会出现ServletResponse、ServletRequest与HttpServletResponse、HttpServletRequest

从继承机构可以看到HttpServletResponse、HttpServletRequest是从HttpServlet中开始出现的,查看HttpServlet源码发现


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

在HttpServlet中将ServletResponse、ServletRequest强转为HttpServletResponse、HttpServletRequest,为什么如此做?

ServletResponse、ServletRequest、HttpServletRequest、HttpServletResponse都只是接口

public interface HttpServletRequest extends ServletRequest

而request对象实际上的类型是org.apache.catalina.connector.RequestFacade
 
Tomcat中的源码

public class RequestFacade implements HttpServletRequest

2、servlet的生命周期中,可以看出,执行的是service方法,为什么我们就只需要写doGet和doPost方法呢?

service()是在javax.servlet.Servlet接口中定义的, 在 javax.servlet.GenericServlet中实现了这个方法, 而 doGet/doPost 则是在 javax.servlet.http.HttpServlet 中实现的。

HttpServlet 里的三个方法:

  • service(HttpServletRequest req, HttpServletResponse resp)
  • doGet(HttpServletRequest req, HttpServletResponse resp)
  • doPost(HttpServletRequest req, HttpServletResponse res)的区别和联系:

在servlet中默认情况下,无论你是get还是post 提交过来 都会经过service()方法来处理,然后转向到doGet 或是doPost方法,我们可以查看HttpServlet 类的service方法: 我在tomcat的lib目录下,解压servlet-api.jar,然后用反编译软件把lib\javax\servlet\http下的HttpServlet.class反编译,看里头的service()方法的原代码:

注意,sun只是定义了servlet接口,而实现servlet接口的就是类似于tomcat的服务器,所以我是在tomcat的安装目录下找到实现的类。


protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String method = req.getMethod(); if(method.equals("GET")) { long lastModified = getLastModified(req); if(lastModified == -1L) { doGet(req, resp); } else { long ifModifiedSince = req.getDateHeader("If-Modified-Since"); if(ifModifiedSince < (lastModified / 1000L) * 1000L) { maybeSetLastModified(resp, lastModified); doGet(req, resp); } else { resp.setStatus(304); } } } else if(method.equals("HEAD")) { long lastModified = getLastModified(req); maybeSetLastModified(resp, lastModified); doHead(req, resp); } else if(method.equals("POST")) doPost(req, resp); else if(method.equals("PUT")) doPut(req, resp); else if(method.equals("DELETE")) doDelete(req, resp); else if(method.equals("OPTIONS")) doOptions(req, resp); else if(method.equals("TRACE")) { doTrace(req, resp); } else { String errMsg = lStrings.getString("http.method_not_implemented"); Object errArgs[] = new Object[1]; errArgs[0] = method; errMsg = MessageFormat.format(errMsg, errArgs); resp.sendError(501, errMsg); } }

从上面可以看出 这里的service是用来转向的,但是如果你在自己的servlet类中覆盖了service方法,比如说你的service是这样的:

public void service(ServletRequest req, ServletResponse res)    
                       throws ServletException, IOException {    
             res.getOutputStream().print(    
             "this is my service");    
  }          

那么这时service就不是用来转向的,而是用来处理业务的,现在不论你的客户端是用pos还是get来请求此servlet

都会执行service方法也只能执行servlet方法,不会去执行doPost或是doGet方法。

3、为什么GenericServlet有两个init方法?

我们可以看到GenericServlet抽象类中

  private transient ServletConfig config;

 public ServletConfig getServletConfig() {
    return config;
    }


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

    public void init() throws ServletException {

    }

init(ServletConfig config)方法,因为只有init(ServletConfig config)中带有ServletConfig对象,为了方便能够在其他地方也能直接使用ServletConfig对象,而不仅仅局限在init(ServletConfig config)方法中,所以创建一个私有的成员变量config,在init(ServletConfig config)方法中就将其赋值给config,然后通过getServletConfig()方法就能够获取ServletConfig对象了

还有一个init()方法,并且这个init()方法是空的,什么都没有,这是为什么呢?这个原因是为了防止一件事情,当我们需要在init方法中做一点别的事情,我们想到的方法就是继承GenericServlet并且重写了init(ServletConfig config)方法,这样依赖,就破坏了原本在GenericServlet类中init(ServletConfig config)写的代码了,也就是在GenericServlet类中的成员变量config会一直是null,无法得到赋值,因为被重写了,就不会在执行GenericServlet中init(ServletConfig config)方法中的代码。要想赋值,就必须在重写的init(ServletConfig config)方法中调用父类的init(ServletConfig config)方法,也就是super.init(ServletConfig config),这样就很麻烦,每次都要手动赋值一遍,所以在GenericServlet类中增加一个init()方法,以后需要在init方法中做什么,只需要重写init()这个方法,而不需要去覆盖init(ServletConfig config)这个方法,这样设计很合理,不用在管init(ServletConfig config)这个方法。

闲言碎语

本篇文章详细介绍了什么是servlet和servlet的生命周期和继承机构,对servlet有了一个详细的了解,但servlet中的几个对象我们还没有详细解释,下篇文章我们将详细研究servlet中的几个特殊对象和servlet的部分使用。

参考

Java Web(一) Servlet详解!!

servlet中service() doGet() doPost() 方法

点赞

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注