我们回顾一下,第一章实现的Web服务器类图大致如下:
其中HttpServer
中的await()
方法会使用一个ServerSocket
来监听8080
端口,用来接收客户端的请求。当接收到用户请求后会创建一个Socket
对象,通过与Socket
关联的InputStream
来创建并填充一个Request
对象(这里只是简单的填充了Uri)。然后用Socket
关联的OutputStream
创建一个Response
对象,并持有一个Request
对象。最后通过调用Response
对象中的sendStaticResource()
方法来向客户端发送静态页面(其中根据request.getUri()
来决定返回哪个页面)。
而第二章实现的Web服务器有什么改进的地方?
首先摒弃了第一章中自定义的Response
和Request
类,而是根据Servlet
标准提供的接口来定义类。Servlet
编程是通过 javax.servlet
和 javax.servlet.http
这两个包的类和接口来实现的。其中一个至关重要的就是javax.servlet.Servlet
接口了。所有的 servlet
必须实现实现或者继承实现该接口的类。Servlet
接口有五个方法:
public interface Servlet {
public void init(ServletConfig config) throws ServletException;
public ServletConfig getServletConfig();
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException;
public String getServletInfo();
public void destroy();
}
其中init
、service
和destory
是Servlet
的生命周期方法。init
只会在Servlet
实例化完成后被调用一次,service
会在每次请求该Servlet
时被调用,而destory
会在该Servlet
卸载时被调用,这通常发生在Servlet
容器正在被关闭或者Servlet
容器需要一些空闲内存的时候。
在第二章中实现的Web服务器将会以Servlet
容器的角度来编程。总的来说,一个全功能的Servlet
容器会为servlet
的每个HTTP请求做下面一些工作:
- 当第一次调用
Servlet
的时候,加载该Servlet
类并调用Servlet
的init
方法(仅一次) - 对每次请求,构造一个
javax.servlet.ServletRequest
实例和一个javax.servlet.ServletResponse
实例 - 调用
servlet
的service
方法,并传入ServletRequest
和ServletReponse
实例 - 当
servlet
容器被关闭时,调用servlet
的destory
方法并卸载servlet
类
那么如何加载Servlet类呢,我们可以用
Class.forName(xxx)
来加载一个类。也可以用java.net.URLClassLoader
类,它是java.lang.ClassLoader
类的一个直接子类。拥有一个URLClassLoader
实例,那么就可以直接使用它的loadClass
方法去加载Servlet
类了。这里我们就直接用URLClassLoader
最简单的构造函数:URLClassLoader(URL[] urls)
,这里的URL
数组就指定了类所在的目录位置(通常叫repository
)。不过由于双亲加载机制,如果在classpath
下已经存在某个类,就加载不到repository
中的对应类了。
刚才说了这里会摒弃第一章的Request
和Response
类。而新的Request
和Response
类分别实现了 javax.servlet.ServletRequest
接口和javax.servlet.ServletResponse
接口,只不过在目前很多方法都留空了。第二章的类图大致如下:
HttpServer
依旧监听8080
端口等待客户端连接,根据请求内容创建并填充Request
对象,并创建Response
对象。Request
和Response
类中的大多数方法都是留空的。然后会根据请求内容来判断是创建ServletProcessor
实例还是创建StaticResourceProcessor
实例,对了,这里的话我个人觉得在两个Processor
之上再创建一个Processor
接口并让它们实现这个接口更加好一些。如果是StaticResourceProcessor
,那么直接返回请求的静态内容。而如果是ServletProcessor
,那么则去加载请求的Servlet
类,然后调用Servlet
的doService
方法来进行处理并响应给客户端。