tomcat学习(2) 一个简单的servlet容器

servlet容器

2.1 javax.servlet.Servlet接口

    servlet编程需要使用到java.servlet和javax.servlet.http两个包下的接口和类,在所有的类和接口中,java.servlet.servlet接口是最重要的,所有的servlet程序都必须实现该接口或者继承实现该接口的类。

接口必须实现的五个方法

    

Servlet接口需要实现下面的5个方法:

         public void init(ServletConfig config) throws ServletException

         public void service(ServletRequest request, ServletResponse response) throws ServletException, java.io.IOException

         public void destroy()

         public ServletConfig getServletConfig()

         public java.lang.String getServletInfo()

    

    在Servlet接口中声明的五个方法里,init(),service(),destory()方法都是servlet生命周期的相关方法,当实例化某个servlet类后,servlet容器会调用其init()方法进行初始化,servlet容器只会调用该方法一次,调用后则可以执行服务方法。在servlet接收任何请求前,必须是经过正确初始化的。servlet程序可以重写此方法。

    当servlet的一个客户端请求到达后,servlet容器就调用响应的servlet的service()方法,并将javax.servlet.servletRequest对象和javax.servlet.servletResponse对象作为参数传入。servletRequest对象包含客户端HTTP请求的信息,servletResponse对象则封装servlet的响应信息。在servlet生命周期,service()可能会被多次调用。

    在将servlet实例从服务中移除前,会调用servlet实例的destroy方法。一般情况下,在服务器关闭前,会发生上述情况,servlet容器会释放内存。只有当servlet实例的service方法中所有的线程都退出或执行超时后,才会调用destroy方法。当容器调用了destroy方法精辟,就不会再调用service方法了。

2.2 javax.servlet.servlet接口

下面从servlet容器的角度观察servlet的开发。在一个全功能servlet容器中,对servlet的每个HTTP请求来说,容器要做下面几件事:

         当第一次调用servlet时,要载入servlet类,调用init方法(仅此一次);

         针对每个request请求,创建一个Request对象和一个Resposne对象;

         调用相应的servlet的service方法,将Request对象和Response对象作为参数传入;

         当关闭servlet时,调用destroy方法,并卸载该servlet类。

这里建立的servlet容器是一个很小的容器,没有实现所有的功能。因此,它仅能运行非常简单的servlet类,无法调用servlet的init和destroy方法。它能执行功能如下所示:

         等待HTTP请求;

         创建Request和Response对象;

         若请求的是一个静态资源,则调用StaticResourceProcessor对象的process方法,传入request和response对象;

         若请求的是servlet,则载入相应的servlet类,调用service方法,传入request对象和response对象。

注意,在这个servlet中,每次请求servlet都会载入servlet类。


大致的流程如下




2.2.1  HttpServer1类

         代码清单如下:

该类与第一章的HttpServer类类似,只是完善了对静态资源和动态资源的处理。

2.2.2  Request类

         代码清单如下:

该类实现了javax.servlet.ServletRequest接口,但并不返回实际内容。

2.2.3  Response类

实现了javax.servlet.ServletResponse接口,大部分方法都返回一个空值,除了getWriter方法以外。

        

         在getWriter方法中,PrintWriter类的构造函数的第二个参数表示是否启用autoFlush。因此,若是设置为false,则如果是servlet的service方法的最后一行调用打印方法,则该打印内容不会被发送到客户端。这个bug会在后续的版本中修改。

2.2.4  StaticResourceProcessor类

该类用于处理对静态资源的请求。

2.2.5  ServletProcessor1类

         该类用于处理对servlet资源的请求。

 

    

该类很简单,只有一个process方法。载入servlet时使用的是UrlClassLoader类,它是ClassLoader类的直接子类,有三种构造方法。


2.3  Application 2

在之前的程序中,有一个严重的问题,必须将ex02.pyrmont.Request和ex02.pyrmont.Response分别转型为javax.servlet.ServletRequest和javax.servlet.ServletResponse,再作为参数传递给具体的servlet的service方法。这样并不安全,熟知servlet容器的人可以将ServletRequest和ServletResponse类向下转型为Request和Response类,并执行parse和sendStaticResource方法。

         一种解决方案是将这两个方法的访问修饰符改为默认的(即,default),这样就可以避免包外访问。另一种更好的方案是使用外观设计模式。uml图如下:


在第二个应用程序中,添加了两个façade类,RequestFacade和ResponseFacade。RequestFacade类实现了ServletRequest接口,通过在其构造方法中传入一个ServletRequest类型引用的Request对象来实例化。ServletRequest接口中每个方法的实现都会调用Request对象的相应方法。但是,ServletRequest对象本身是private类型,这样就不能从类的外部进行访问。这里也不再将Request对象向上转型为ServletRequest对象,而是创建一个RequestFacade对象,并把它传给service方法。这样,就算是将在servlet中获取了ServletRequest对象,并向下转型为RequestFacade对象,也不能再访问ServletRequest接口中的方法了,就可以避免前面所说的安全问题。

         RequestFacade.java代码如下:

注意它的构造函数,接收一个Request对象,然后向上转型为ServletRequest对象,赋给其private成员变量request。该类的其他方法中,都是调用request的相应方法实现的,这样就将ServletRequest完整的封装得RequestFacade中了。

         同理,ResponseFacade类也是这样的。

         Application 2中的类包括,HttpServer2、Request、Response、StaticResourceProcessor、ServletProcessor2、Constants。



源码路径:

 https://download.csdn.net/download/dalton2017/10475513

猜你喜欢

转载自blog.csdn.net/Dalton2017/article/details/80674624