源码学习-Tomcat-02-一个简单的Servlet容器

本文会尝试介绍怎么实现一个简单的响应静态资源请求,或者servlet请求的Servlet容器。

Web 服务器也称为超文本传输协议(HTTP)服务器,因为它使用 HTTP 来跟客户端进行通信的,这通常是个 web 浏览器。一个基于 java 的 web 服务器使用两个重要的类:java.net.Socket 和 java.net.ServerSocket,并通过 HTTP 消息进行通信。

一个全功能的 servlet 容器会为 servlet 的每个 HTTP 请求做下面一些工作:

  • 当第一次调用 servlet 的时候,加载该 servlet 类并调用 servlet 的 init 方法(仅仅一次)。

  • 对每次请求,构造一个 javax.servlet.ServletRequest 实例和一个javax.servlet.ServletResponse 实例。
  • 调用 servlet 的 service 方法,同时传递 ServletRequest 和 ServletResponse 对象。
  • 当 servlet 类被关闭的时候,调用 servlet 的 destroy 方法并卸载 servlet 类。

本文的 servlet 容器不是全功能的。因此,她不能运行什么除了非常简单的 servlet,而且也不调用 servlet 的 init 方法和 destroy 方法。相反它做了下面的事情:

  • 等待 HTTP 请求。

  • 构造一个 ServletRequest 对象和一个 ServletResponse 对象。
  • 假如该请求需要一个静态资源的话,调用 StaticResourceProcessor 实例的 process 方法,同时传递 ServletRequest 和 ServletResponse 对象。
  • 假如该请求需要一个 servlet 的话,加载 servlet 类并调用 servlet 的 service 方法,同时传递 ServletRequest 和 ServletResponse 对象。

本实现的Servlet容器类图如下:

本实现的Servlet容器时序图如下:

本实现的main函数在HttpServer1类中,

预备知识:

  • HTTP  的请求格式:

方法—统一资源标识符(URI)—协议/版本

请求的头部

主体内容

比如:

GET /index.html HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0) QQBrowser/8.2.4258.400
Accept-Encoding: gzip, deflate
Host: localhost:8080
Connection: Keep-Alive

name=shouhui

  • jvm类加载机制:

操作:

访问指定静态页面:http://localhost:8080/index.html

访问指定Servlet类:http://localhost:8080/servlet/PrimitiveServlet

停止该Servlet容器的运行:http://localhost:8080/SHUTDOWN

参考文章:

百度百科,servlet: http://baike.baidu.com/link?url=-WtPi8Z5ygr6g1gGDmtM3ObRwVwhoR7h-7lmBR-KX8kO5GsrTIxHtvkrMG2TW4MjtRRjUGnTfbCT9Fy2y0v4Sq

Budi Kurniawan, Paul Deck. 深入剖析Tomcat (M). 北京: 机械工业出版社华章公司, 2011.

参考代码:

public class HttpServer1 {

    /**
     * WEB_ROOT is the directory where our HTML and other files reside. For this
     * package, WEB_ROOT is the "webroot" directory under the working directory.
     * The working directory is the location in the file system from where the
     * java command was invoked.
     */
    // shutdown command
    private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";

    // the shutdown command received
    private boolean shutdown = false;

    public static void main(String[] args) {
        HttpServer1 server = new HttpServer1();
        server.await();
    }

    public void await() {
        ServerSocket serverSocket = null;
        int port = 8080;
        try {
            serverSocket = new ServerSocket(port, 1,
                    InetAddress.getByName("127.0.0.1"));
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(1);
        }

        // Loop waiting for a request
        while (!shutdown) {
            Socket socket = null;
            InputStream input = null;
            OutputStream output = null;
            try {
                socket = serverSocket.accept();
                input = socket.getInputStream();
                output = socket.getOutputStream();

                // create Request object and parse
                Request request = new Request(input);
                request.parse();

                // create Response object
                Response response = new Response(output);
                response.setRequest(request);

                // check if this is a request for a servlet or a static resource
                // a request for a servlet begins with "/servlet/"
                if (request.getUri().startsWith("/servlet/")) {
                    ServletProcessor1 processor = new ServletProcessor1();
                    processor.process(request, response);
                } else {
                    StaticResourceProcessor processor = new StaticResourceProcessor();
                    processor.process(request, response);
                }

                // Close the socket
                socket.close();
                // check if the previous URI is a shutdown command
                shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
            } catch (Exception e) {
                e.printStackTrace();
                System.exit(1);
            }
        }
    }
}
public class Request implements ServletRequest {

    private InputStream input;
    private String uri;

    public Request(InputStream input) {
        this.input = input;
    }

    public String getUri() {
        return uri;
    }

    private String parseUri(String requestString) {
        int index1, index2;
        index1 = requestString.indexOf(‘ ‘);
        if (index1 != -1) {
            index2 = requestString.indexOf(‘ ‘, index1 + 1);
            if (index2 > index1)
                return requestString.substring(index1 + 1, index2);
        }
        return null;
    }

    public void parse() {
        // Read a set of characters from the socket
        StringBuffer request = new StringBuffer(2048);
        int i;
        byte[] buffer = new byte[2048];
        try {
            i = input.read(buffer);
        } catch (IOException e) {
            e.printStackTrace();
            i = -1;
        }
        for (int j = 0; j < i; j++) {
            request.append((char) buffer[j]);
        }
        System.out.print(request.toString());
        uri = parseUri(request.toString());
    }

    /* implementation of the ServletRequest */
    public Object getAttribute(String attribute) {
        return null;
    }

    public Enumeration getAttributeNames() {
        return null;
    }

    public String getRealPath(String path) {
        return null;
    }

    public RequestDispatcher getRequestDispatcher(String path) {
        return null;
    }

    public boolean isSecure() {
        return false;
    }

    public String getCharacterEncoding() {
        return null;
    }

    public int getContentLength() {
        return 0;
    }

    public String getContentType() {
        return null;
    }

    public ServletInputStream getInputStream() throws IOException {
        return null;
    }

    public Locale getLocale() {
        return null;
    }

    public Enumeration getLocales() {
        return null;
    }

    public String getParameter(String name) {
        return null;
    }

    public Map getParameterMap() {
        return null;
    }

    public Enumeration getParameterNames() {
        return null;
    }

    public String[] getParameterValues(String parameter) {
        return null;
    }

    public String getProtocol() {
        return null;
    }

    public BufferedReader getReader() throws IOException {
        return null;
    }

    public String getRemoteAddr() {
        return null;
    }

    public String getRemoteHost() {
        return null;
    }

    public String getScheme() {
        return null;
    }

    public String getServerName() {
        return null;
    }

    public int getServerPort() {
        return 0;
    }

    public void removeAttribute(String attribute) {
    }

    public void setAttribute(String key, Object value) {
    }

    public void setCharacterEncoding(String encoding)
            throws UnsupportedEncodingException {
    }

    @Override
    public int getRemotePort() {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public String getLocalName() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String getLocalAddr() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int getLocalPort() {
        // TODO Auto-generated method stub
        return 0;
    }

}
public class Response implements ServletResponse {

    private static final int BUFFER_SIZE = 1024;
    Request request;
    OutputStream output;
    PrintWriter writer;

    public Response(OutputStream output) {
        this.output = output;
    }

    public void setRequest(Request request) {
        this.request = request;
    }

    /* This method is used to serve a static page */
    public void sendStaticResource() throws IOException {
        byte[] bytes = new byte[BUFFER_SIZE];
        FileInputStream fis = null;
        try {
            /* request.getUri has been replaced by request.getRequestURI */
            File file = new File(Constants.WEB_ROOT, request.getUri());
            fis = new FileInputStream(file);
            /*
             * HTTP Response = Status-Line(( general-header | response-header |
             * entity-header ) CRLF) CRLF [ message-body ] Status-Line =
             * HTTP-Version SP Status-Code SP Reason-Phrase CRLF
             */
            int ch = fis.read(bytes, 0, BUFFER_SIZE);
            while (ch != -1) {
                output.write(bytes, 0, ch);
                ch = fis.read(bytes, 0, BUFFER_SIZE);
            }
        } catch (FileNotFoundException e) {
            String errorMessage = "HTTP/1.1 404 File Not Found\r\n"
                    + "Content-Type: text/html\r\n" + "Content-Length: 23\r\n"
                    + "\r\n" + "<h1>File Not Found</h1>";
            output.write(errorMessage.getBytes());
        } finally {
            if (fis != null)
                fis.close();
        }
    }

    /** implementation of ServletResponse */
    public void flushBuffer() throws IOException {
    }

    public int getBufferSize() {
        return 0;
    }

    public String getCharacterEncoding() {
        return null;
    }

    public Locale getLocale() {
        return null;
    }

    public ServletOutputStream getOutputStream() throws IOException {
        return null;
    }

    public PrintWriter getWriter() throws IOException {
        // autoflush is true, println() will flush,
        // but print() will not.
        writer = new PrintWriter(output, true);
        return writer;
    }

    public boolean isCommitted() {
        return false;
    }

    public void reset() {
    }

    public void resetBuffer() {
    }

    public void setBufferSize(int size) {
    }

    public void setContentLength(int length) {
    }

    public void setContentType(String type) {
    }

    public void setLocale(Locale locale) {
    }

    @Override
    public String getContentType() {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void setCharacterEncoding(String charset) {
        // TODO Auto-generated method stub

    }
}
public class ServletProcessor1 {

    public void process(Request request, Response response) {

        String uri = request.getUri();
        String servletName = uri.substring(uri.lastIndexOf("/") + 1);
        URLClassLoader loader = null;

        try {
            // create a URLClassLoader
            URL[] urls = new URL[1];
            URLStreamHandler streamHandler = null;
            File classPath = new File(Constants.WEB_ROOT);
            // the forming of repository is taken from the createClassLoader
            // method in
            // org.apache.catalina.startup.ClassLoaderFactory
            String repository = (new URL("file", null,
                    classPath.getCanonicalPath() + File.separator)).toString();
            // the code for forming the URL is taken from the addRepository
            // method in
            // org.apache.catalina.loader.StandardClassLoader class.
            urls[0] = new URL(null, repository, streamHandler);
            loader = new URLClassLoader(urls);
        } catch (IOException e) {
            System.out.println(e.toString());
        }
        Class myClass = null;
        try {
            myClass = loader.loadClass(servletName);
        } catch (ClassNotFoundException e) {
            System.out.println(e.toString());
        }

        Servlet servlet = null;

        try {
            servlet = (Servlet) myClass.newInstance();
            servlet.service((ServletRequest) request,
                    (ServletResponse) response);
        } catch (Exception e) {
            System.out.println(e.toString());
        } catch (Throwable e) {
            System.out.println(e.toString());
        }

    }
}
public class StaticResourceProcessor {

    public void process(Request request, Response response) {
        try {
            response.sendStaticResource();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
时间: 2024-11-08 21:03:44

源码学习-Tomcat-02-一个简单的Servlet容器的相关文章

Vue2.x源码学习笔记-从一个小例子查看vm实例生命周期

学习任何一门框架,都不可能一股脑儿的从入口代码从上到下,把代码看完, 这样其实是很枯燥的,我想也很少有人这么干,或者这么干着干着可能干不下去了. 因为肯定很无聊. 我们先从一个最最简单的小例子,来查看new Vue(options)实例,这个过程发生了什么. vm实例上的属性又如何添加上去的,又如何渲染到浏览器页面上的. 关于vue的数据依赖和虚拟dom都是重点,必然会在以后的帖子记录. 这篇帖子就根据下例子,看看实例化一个vm实例做了啥吧. 先把小例子贴出来: <div id="app&

Tomcat学习笔记(二)—— 一个简单的Servlet容器

1.简介:Servlet编程是通过javax.Servlet和javax.servlet.http这两个包的类和接口实现的,其中javax.servlet.Servlet接口至关重要,所有的Servlet必须实现该接口或者继承实现了该接口的类. 2.Servlet接口有5个方法: public void init(ServletConfig config) throws ServletException; public void service(ServletRequest request, S

how tomcat works 读书笔记(二)----------一个简单的servlet容器

app1 (建议读者在看本章之前,先看how tomcat works 读书笔记(一)----------一个简单的web服务器 http://blog.csdn.net/dlf123321/article/details/39378157) 回顾我们上一章,我们开发了一个最最简单的web服务器,它可以使用户访问服务器内的静态资源.当然这是远远不够的,在这一节里,我们就试着让服务器在能相应静态资源的基础上继续支持servlet. servlet接口 javax.servlet.Servlet接口

Servlet学习教程(三)---- 一个简单的Servlet例子

我们用个最简单的Servlet例子来解说一下Servlet简单配置以及Servlet类实现类的写法. 第一,我们新建一个Dynamic Web Project,起名Servlet 点击NEXT,设置Default output folder 为Servlet/WebContent/WEB-INF/classes 第二,创建一个包,包名为Servlet,然后创建一个类名为WelcomeServlet类.(Servlet类当然缺少不了Servlet容器,请注意你的开发软件是否已经集成Servlet容

一个简单的Servlet容器实现[一]

上篇写了一个简单的Java web服务器实现,只能处理一些静态资源的请求,本篇文章实现的Servlet容器基于前面的服务器做了个小改造,增加了Servlet请求的处理. 程序执行步骤 创建一个ServerSocket对象: 调用ServerSocket对象的accept方法,等待连接,连接成功会返回一个Socket对象,否则一直阻塞等待: 从Socket对象中获取InputStream和OutputStream字节流,这两个流分别对应request请求和response响应: 处理请求:读取In

2.一个简单的Servlet容器

很显然Http服务器会按照Http协议对获取的请求进行分析并返回规定的信息,通过对Http的理解,java大叔将其返回的信息封装成一个类,称为Servlet,至此,Http服务器在Java中就变成了按照Http协议对获取的请求进行分析并返Servlet,很明显,Http服务器在这里也就变为了Servlet容器...    注:服务器是什么?理解万岁...

一行一行分析JQ源码学习笔记-02

1.防止冲突    设置新变量保存??   $ = jquery 2. jquery = function() { return new jqery.fn.init () } 3. 为什么构造函数有引用关系 例如: jquery().css() 构造函数 jquery.prototype.init.prototype = jquery.prototype; //对象引用对象

攻城狮在路上(肆)How tomcat works(二) 一个简单的servlet容器

该节在上一节的基础上增加了所谓对静态资源和动态资源访问的不同控制流程.示例里面采用的是对路径“/servlet/”进行了特殊处理. 一. 主要还是从HttpServer1中的main方法开始,先解析出Request和Response对象,然后如果请求路径中包含/servlet/路径的话, 就进入ServletProcesser1进行处理,否则就进入StaticProcessor进行处理. 在ServletProcessor1中,对Request和Response进行了封装,通过URLClassL

深入剖析Tomcat-第二章:一个简单的servlet容器(2)

内容:在上一篇中存在一些安全问题,因为我们需要将request和response想上转型为ServletRequest和ServletResponse,了解这个servlet容器内部工作原理的servlet程序员可以将ServletRequest和ServletResponse分别向下转型然后就能调用Request和Response的parse()和sendStaticResource()了.所以我们需要利用外观类来解决,添加两个RequestFacade和ResponseFacade,然后分别

深入剖析Tomcat-第二章:一个简单的servlet容器(1)

内容:在上一章的基础上多了对servlet的处理,重点是ServletProcessor1这个类,利用到了URL.URLClassLoader(可以参看文档)和java反射机制. webroot: public class HttpServer1 { private static final String SHUTDOWN_COMMAND = "/SHUTDOWN"; private boolean shutdown = false; public void await() { Ser