(三)Servlet就是这样-Servlet的一些细节

Servlet读书笔记系列文《Servlet就是这样》第三篇:Servlet的一些细节

细节一:Servlet简介

l  Servlet是sun公司提供的一门用于开发动态web资源的技术

l  Sun公司在其API中提供了一个servlet接口,用户若想开发一个动态web资源(即开发一个Java程序向浏览器输出数据),需要完成以下2个步骤:

1)       编写一个Java类,实现servlet接口。

2)       把开发好的Java类部署到web服务器中。

细节二:Servlet的运行过程

l  Servlet程序是由WEB服务器调用,web服务器收到客户端的Servlet访问请求后:

1)       Web服务器首先检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第④步,否则,执行第②步。

2)       装载并创建该Servlet的一个实例对象。

3)       调用Servlet实例对象的init()方法。

4)       创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。

5)       WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。

细节三:Servlet的生命周期

l  生命周期的概念:一件事物,什么时候生,什么时候死,以及在其生存阶段的某一时点会触发的事件,统称为该事物的生命周期。

l  Servlet的生命周期:

1.       通常情况下,服务器会在Servlet第一次被调用时创建该Servlet类的实例对象(servlet出生);一旦被创建出来,该Servlet实例就会驻留在内存中,为后续请求服务;直至web容器退出,servlet实例对象才会被销毁(servlet死亡)。

2.       在Servlet的整个生命周期内,Servlet的init方法只有在servlet被创建时被调用一次。

3.       而对一个Servlet的每次访问请求都导致Servlet引擎调用一次servlet的service方法。对于每次访问请求,Servlet引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调用的Servlet的service()方法,service方法再根据请求方式分别调用doXXX方法。

4.       servlet被销毁前,会调用destroy()方法。

细节四:Servlet接口实现类

l  当编写一个servlet时,必须直接或间接实现servlet接口,Servlet接口SUN公司定义了两个默认实现类,分别为:GenericServlet、HttpServlet。要实现javax.servlet.Servlet接口(即写自己的Servlet应用),你可以写一个继承自javax.servlet.GenericServletr的Generic Servlet ,也可以写一个继承自java.servlet.http.HttpServlet的HTTP Servlet(这就是为什么我们自定义的Servlet通常是extentdsHttpServlet的)

l  查看Servlet的官方API,Servlet有5个要实现的方法,如下:

1)       init(servletconfig   config)  

2)       service(servletrequest   req,servletresponse   resp)  

3)       destroy()  

4)       getservletconfig()  

5)       getservletinfo()

l  我们查看下ServletAPI,其中是这样描述 GenericServlet 的:

GenericServlet 定义了一个通用的,无关协议的的Servlet 。如果要在 Web 应用中使用 Http 进行 Servlet 通信,请扩展 HttpServlet (即继承 HttpServlet )

l  我们再查看下GenericServlet、HttpServlet它们的源码:

GenericServlet

public abstractclass GenericServlet implements Servlet,ServletConfig, Serializable {

……

……

       public void destroy(){}

public String getInitParameter(String name){}

public Enumeration getInitParameterNames(){}

public ServletConfig getServletConfig(){}

public ServletContext getServletContext(){}

public String getServletInfo(){}

public void init(ServletConfig config){}

public void init(){}

public void log(String msg){}

public void log(String message, Throwable t){}

public String getServletName(){}

public abstractvoid service(ServletRequest paramServletRequest,ServletResponse paramServletResponse) throws ServletException, IOException;

       }

       HttpServlet

       public abstract class HttpServlet extendsGenericServlet implements Serializable {

       ……

……

protected long getLastModified(HttpServletRequestreq) {

protected void doHead(HttpServletRequest req,HttpServletResponse resp)

protected void doPost(HttpServletRequest req,HttpServletResponse resp)

     throws ServletException, IOException {

protected void doGet(HttpServletRequest req,HttpServletResponse resp)

     throws ServletException, IOException {

protected void doPut(HttpServletRequest req,HttpServletResponse resp)

     throws ServletException, IOException {

protected void doDelete(HttpServletRequest req,HttpServletResponse resp)

     throws ServletException, IOException {

private static Method[] getAllDeclaredMethods(Classc) {

protected void doOptions(HttpServletRequest req,HttpServletResponse resp)

     throws ServletException, IOException {

protected void doTrace(HttpServletRequest req,HttpServletResponse resp)

     throws ServletException, IOException {

protectedvoid service(HttpServletRequest req, HttpServletResponse resp)

      throws ServletException, IOException {

Stringmethod = 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);

    }

  }

private void maybeSetLastModified(HttpServletResponseresp,

     long lastModified) {

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

  }

}

请注意查看上面这两个类的源码(PS:这两个类中的大部分方法我只是写出了它们的方法头,简短,方便查看),我们注意到,这两个类是抽象类,不能直接进行实例化,必须给出子类才能实例化(注意这一块),它们的类中分别有一个抽象方法,就是service()方法,这个是要我们手动实现的。

l  总得来看,GenericServlet给出了设计 servlet的一些骨架,定义了 servlet 生命周期,还有一些得到名字、配置、初始化参数的方法,其设计的是和应用层协议无关的,也就是说你有可能用非 http 协议实现它(其实目前 Java Servlet 还是只有 Http 一种)。

HttpServlet 是采用 Http 协议进行通信的,所以它也实现 Http 协议中的多种方法,HttpServlet 的 service() 方法比较特殊, public 关键字的 service() 方法明显是继承自父类,它只接收 HTTP 请求,这里把相应的 request response 转换为了基于 HTTP 协议的相应对象最终将请求转到带 protected 关键字的 service() 方法中。protected service() 方法根据请求的类型将请求转发到相应的 doDelete() doGet() doOptions() doPost() doPut() 等方法中所以开发自己的 Servlet 时,不需要覆盖HttpServlet service() 方法,因为该方法最终将请求转发相相应的 doXXX 方法中,只需要覆盖相应的 doXXX 方法进行请求处理即可。如果重写了该方法,那么就不会根据方法名调用其他具体的方法了。

细节五:Servlet映射URL

由于客户端是通过URL地址访问web服务器中的资源,所以Servlet程序若想被外界访问,必须把servlet程序映射到一个URL地址上,这个工作在web.xml文件中使用<servlet>元素和<servlet-mapping>元素完成。

l  <servlet>元素用于注册Servlet,它包含有两个主要的子元素:<servlet-name>和<servlet-class>,分别用于设置Servlet的注册名称和Servlet的完整类名。

l  一个<servlet-mapping>元素用于映射一个已注册的Servlet的一个对外访问路径,它包含有两个子元素:<servlet-name>和<url-pattern>,分别用于指定Servlet的注册名称和Servlet的对外访问路径。

细节六:Servlet的调用和生命周期

l  Servlet是一个供其他Java程序(Servlet引擎)调用的Java类,它不能独立运行,它的运行完全由Servlet引擎来控制和调度

l  针对客户端的多次Servlet请求,通常情况下,服务器只会创建一个Servlet实例对象,也就是说Servlet实例对象一旦创建,它就会驻留在内存中,为后续的其它请求服务,直至web容器退出,servlet实例对象才会销毁。

l  在Servlet的整个生命周期内,Servlet的init方法只被调用一次。而对一个Servlet的每次访问请求都导致Servlet引擎调用一次servlet的service方法。对于每次访问请求,Servlet引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调用的Servlet的service()方法,service方法再根据请求方式分别调用doXXX方法。

细节七:Servlet的默认映射

l  如果某个Servlet的映射路径仅仅为一个正斜杠(/),那么这个Servlet就成为当前Web应用程序的缺省Servlet。

l  凡是在web.xml文件中找不到匹配的<servlet-mapping>元素的URL,它们的访问请求都将交给缺省Servlet处理,也就是说,缺省Servlet用于处理所有其他Servlet都不处理的访问请求。

l  在<tomcat的安装目录>\conf\web.xml文件中,注册了一个名称为org.apache.catalina.servlets.DefaultServlet的Servlet,并将这个Servlet设置为了缺省Servlet。

l  当访问Tomcat服务器中的某个静态HTML文件和图片时,实际上是在访问这个缺省Servlet。

细节八:线程安全

l  当多个客户端并发访问同一个Servlet时,web服务器会为每一个客户端的访问请求创建一个线程,并在这个线程上调用Servlet的service方法,因此service方法内如果访问了同一个资源的话,就有可能引发线程安全问题。

l  如果某个Servlet实现了SingleThreadModel接口,那么Servlet引擎将以单线程模式来调用其service方法。

l  SingleThreadModel接口中没有定义任何方法,只要在Servlet类的定义中增加实现SingleThreadModel接口的声明即可。 

l  对于实现了SingleThreadModel接口的Servlet,Servlet引擎仍然支持对该Servlet的多线程并发访问,其采用的方式是产生多个Servlet实例对象,并发的每个线程分别调用一个独立的Servlet实例对象。

l  实现SingleThreadModel接口并不能真正解决Servlet的线程安全问题,因为Servlet引擎会创建多个Servlet实例对象,而真正意义上解决多线程安全问题是指一个Servlet实例对象被多个线程同时调用的问题。事实上,在Servlet API 2.4中,已经将SingleThreadModel标记为Deprecated(过时的)。  

细节九:ServletContext

l  WEB容器在启动时,它会为每个WEB应用程序都创建一个对应的ServletContext对象,它代表当前web应用。

l  ServletConfig对象中维护了ServletContext对象的引用,开发人员在编写servlet时,可以通过ServletConfig.getServletContext方法获得ServletContext对象。

l  由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象。

l  查看ServletContextAPI文档,了解ServletContext对象的功能。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值