Servlet(一)--简单的Servlet容器源码分析

从源码角度分析Servlet

一.Servlet简介

    Servlet架构

    1. servlet是什么?

        Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。

    2. TomCat作为Servlet容器

        TomCat可以搭建一个HTTP服务器作为Servlet容器。

        Sevrlet容器具有3个基本任务:

        创建一个request对象,用可能会在调用的Servlet中使用到的信息,如参数,头,cookie,查询字符串,URI等。Request对象是javax.servlet.ServletRequest接口或javax.servlet.http.ServletRequest接口的一个实例

        创建一个调用Servletresponse对象,用来向Web客户端发送响应,response对象是javax.servlet.ServletResponse接口或javax.servlet.http.ServletResponse接口的一个实例。

        调用Servletservice()方法,将request对象和response对象作为参数传入。Servletrequest对象读取信息,并通过response对象发送响应信息。

 

        一个Servlet容器可以由多个Servlet程序用来服务不同的HTTP请求(即使用service()方法服务),并且对于每个的Servlet程序由许多类组成,这些类或多或少使用javax.servlet.ServletRequestjavax.servlet.http.ServletRequest的对象。

    3. servlet的生命周期

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

        Servlet接口中声明了5个方法,其签名如下:

            1.Public void init(ServletConfig config) throws ServletException

  2.Public void service(ServletRequest request , ServletResponse response) Throws ServletException,java.io.IOException

            3.Public void destroy()

            4.Public ServletConfig getServletConfig()

            5.Public java.lang.String getServletInfo()

             Init()方法

            当Servlet容器实例化某个servlet类后,Servlet容器将会调用其init()方法进行初始化。Servlet容器只会调用一次init()方法,初始化代码块比如连接数据库,设置一些初始值等。

            Service()方法

            当Servlet容器初始化完成后会调用service()方法执行相应的服务信息。

           比如,当一个或多个客户端请求达到后,Servlet容器为每个客户端创建一个线程,并且这些线程将会同步的调用service()方法。并且将相应的javax.servlet.ServletRequestjavax.servlet.ServletResponse参数传递进来。ServletRequest对象包含了客户端的HTTP请求的信息,ServletResponse对象则封装servlet响应。在servlet对象的生命周期内,service()方法将会多次调用。

            Destroy()方法

            当Servlet容器关闭或Servlet容器要释放内存时,才会将servlet实例移除,而且只有当servlet实例的service()方法中的所有线程都退出或执行超时后,才会调用destroy()方法。当Servlet容器调用了某个实例的servlet实例的destroy()后,它将不会再拥有该实例servlet实例的service()方法。调用destroy()方法后,让servlet对象有机会清理自身持有的资源,如内存,文件句柄和线程等。

Servlet对象生命周期


二、源码分析

    对一个Servlet的每个HTTP请求,一个功能齐全的servlet容器需要做下面几件事:

       1. 当一次调用某个servlet时,要载入该servlet类,并调用其init()方法(仅此一次)

       2. 针对每个request请求,创建一个javax.servlet.ServletRequest实例和一个javax.servlet.ServletResponse实例

       3. 调用该servletservice()方法,将servletRequest对象和servletResponse对象作为参数传入。

       4. 当关闭该servlet类时,调用其destroy()方法,并卸载该类


    

    下面是一个简单的servlet容器,它将做下面几件事:

        1. 等待HTTP请求

        2. 创建一个servletRequest对象和一个servletResponse对象

      3. 若请求是静态资源,则调用StaticResouresProcess对象的process()方法,传入servletRequest对象和servletRespones对象

        4. 若请求的是servlet,则载入相应的servlet类,调用其service()方法,传入servletRequest对象和servletResponse对象。

    另外,在该servlet容器中,每次请求的servlet都会载入相应的servlet类。

    

    类导图:

        l HttpServer1.java

        l Request.java

        l Response.java

        l StaticResouresProcess.java

        l ServletProcess1.java

        l Constants.java


    1. HttpServer1

        1)使用的JAVA类库

        

        2)类的常量字段

            

            HttpServer1类中变量字段,SHUTDOWN_COMMAD字段用关闭命令。Shutdown用作await()方法中循环判断字段,其作用主要是根据URLSHUTDOWN_COMMAD”字段是否有写关闭命令来显性关闭服务器响应。

        3)方法字段

            

          在这片段的await()方法字段中,主要是进行服务器端的初始化工作。服务器端,通过实例化serverSocket对象,调用accept()方法监听指定的端口,与客户端进行连接。

            ①.serverSocket的构造方法

                

            ①.Accept()方法

                public Socket accept() throws IOException

                侦听并接受到此套接字的连接。此方法在连接传入之前一直阻塞。

            ①.JAVA IO的使用

            当客户端-服务器端连接建立,客户端的请求信息,和服务器的响应信息,都可以通过IO对数据流的输入输出。

                

                在该await()方法片段中,通过用户URL显视关闭循环,并且调用accept()方法,获取与客户端连接。当连接建立好之后,实例化相应的输入输出流(在之后的编程中,将围绕着,这两个输入输出流进行编程)

                在这里面,将实例化RequestResponse类,其中Request.parse()方法用于解析URL

                

                在剩下的部分中,通过条件判断,请求的是静态资源还是servlet服务的资源。最后根据用户的URL判断是否关闭服务器响应。

    2. Request

    对于一个Servlet容器中,由于HTTP请求,它将实例化一个javax.servlet.ServletRequest和一个javax.servlet.ServletResponse的实例。对于服务相应的HTTP请求的Servlet程序,调用它的service()方法,并且将上述实例传递给它。

    对于Request类,表示被传递给Servletservice()方法的一个request对象,所以它必须要实现javax.servlet.ServletRequest接口中所声明的所有方法(在后面的程序中将会继续给出)

        1)使用的JAVA类库

        

        2)声明的类变量成员

        

        3)类中的三个公共方法

            一个是public Request(InputStream input)构造函数,用于得到输入流的实例对象。

            另一个是public String getUri() 用于其他类的公共方法,作用是返回解析后的字符串信息。

            最后一个Request类的公共方法public void parse() 用于在HtpServer类中调用该方法,从客户端请求中获取URL字段。InputStream类的read()方法可以把输入流数据串行化放到相应的字节区中。

            

              

        4Request类私有方法

            用途是传递客户端读入流字节信息,并且解析相应的字串,解析标准是根据HTTP请求报文格式。比如,在请求报文中,URL字串在方法+空格后,和空格+协议版本前面。

            

    3. Response

        这个类将对客户端请求的URL字段中请求的文件,返回给客户端。

        若文件不存在,则返回404状态码错误信息。

        Response类实现了javax.servlet.ServletResponse接口,需要复写其中的方法,

        并且作为参数需要传递个service()方法中的一个参数

        1) Response使用的类库

            

        2) Response类的变量字段

            

        3) Response类的公共方法

            

         

            public Response(OutputStream output)公共方法是Response类的构造函数主要用于获得从Httpserver1类获得的实例化输出流。

            public void setRequest(Request request) 公共方法将会接受一个Request类的实例对象

            public void sendStaticResource()公共方法用于发送一个静态资源文件给客户端,比如这里发送的是index.html文件。

           首先,该文件通过WEB_ROOT获得用户的父路径比如说:”G:\计算机学习文献\Java\Tomcat\HowTomcatWorks\webroot”路径,然后在通过客户端请求的子路径URL实例化一个File对象。下图是File对象的构造函数

            

            

            

            

    4. StaticResourceProcesser

    该类主要用于处理静态资源的请求,这个类只有一个公共方法,即process()方法,该方法接受Request,Response类的实例对象,调用Response.sendStaticResource()方法

        

    5. servletProcessor1

        servletProcessor1类主要用于客户端通过servlet容器访问相关资源时,该怎么调用service()方法服务相应的客户端请求。

      该类只有一个方法process()方法,该方法接受的两个参数,一个是javax.servlet.ServletRequest实例,和javax.servlet.ServletResponse实例。

        

        这一部分是实现一个类载入器。

        

        剩下的部分通过类载入器,获取servlet容器中创建的servlet实例字段。然后把Request类的实例,Response类的实例向上转型为ServletRequest实例,和ServletResponse的实例。再调取service()方法。通过这样的形式,响应客户端的请求。

    6. Constants

        这个类主要用于获取服务器的文件内容路径

        

        File.separator将打印一个\的分隔符.webroot是服务器文件存放地。

三. 存在的问题

    对于servletProcessor1类中,service()方法其传递的参数是Request(实现了javax.servlet.ServletRequest接口)向上转类型的ServletRequest,和Response(实现了javax.servlet.ServletResponse接口)向上转类型的ServletResponse

    这是一种很不安全的方法,为什么?

    因为在service()方法中,将继续可以使用Request类中声明的公共方法getUrl()parse()方法和Response类中声明的公共方法sendStaticReource()方法。显然,对于这两个方法我们希望在其他类,甚至其他包使用(不修改访问权限)。但我们不想在service()方法使用,若在service()方法中使用,将产生不可预知的错误。

    可以做的改变:

        1. Request类和Response类中的公共方法修改其访问权限

            比如说把公共方法修改为受保护的方法,因此可以解决这个问题,但却不是我们想要的结果。

        2. 使用外观类

        添加两个外观类,RequestFacade(实现了javax.servlet.ServletRequest接口)类和ResponseFacade(实现了javax.servlet.ServletResponse接口)类,其构造函数传递Request类的实例,和Response的实例,通过把javax.servlet.ServletRequest实例向下转换为Request类实例。

            在通过把RequestFacade实例对象向上转换为javax.servlet.ServletRequest对象传递给service()方法中,这样service()方法中只能使用javax.servlet.ServlerRequest接口中定义的方法。这样可以不需修改类方法访问权限,也能避免相应的问题。

            对于Response类也类似。

    1. RequestFacade

        

        这个类ServletRequest对象被定义成私有的,很明显,在实际使用时可以避免发生必要的错误

    2. ResponseFacade

        

        同上面

    3. 修改的ServletProcessor2

        

        这是相应的修改字段,对于service()方法,我们只需要传递RequestFacadeResponseFacade类的实例即可

    4. 修改的HttpServer2

        

        实例化产生一些改变








































阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页