上一篇文章里分析了一个简单的Web容器的实现过程,今天笔者来分析一个servlet容器是如何实现的。相信通过这两篇博客读者就可以理解为什么说Tomcat是一个容器了,既是Web容器又是servlet容器。所谓Web容器仅仅是可以处理静态资源(html)但是servlet就不一样了,由于在servlet里面我们可以按照servlet的规范编写我们的逻辑所以就出现了变化无穷的网页。所谓的Web2.0正是基于这种动态的网页技术来实现的,互联网发展到今天每个阵营拥有自己的一套成熟处理方式,微软用的是aspjava用的是servletjsp技术。其实大同小异,都是将逻辑放在服务器中,然后动态的生成html,进而将生成的html传输到客户端,最终这些html代码被浏览器解析最终呈现出色彩斑斓的网页。

说远了,接着回来说咱的servlet容器。

相信绝大多数读者都写过servlet类,所有的Servlet程序都需要实现javax.servlet.servlet接口。(关于servlet接口在这里就不赘述了,我们需要了解的是servlet5个方法当中init(),service()destroy()方法分别是在servlet的哪个生命周期调用的。)

首先看HttpServer2当中的核心代码,和上一篇介绍Web容器的中的HttpServer代码类似都是等待一个请求然后创建RequestResponse对象。不同的是这里进行判断是请求静态资源还是请求一个Servlet

if (request.getUri().startsWith("/servlet/")) {
                    ServletProcessor2 processor = new ServletProcessor2();
                    processor.process(request, response);
                } else {
                    StaticResourceProcessor processor = new StaticResourceProcessor();
                    processor.process(request, response);
                }

如果客户端请求的是servlet,那么交给Processor进行处理。在处理请求的过程中Servlet容器和Web容器不同的是:后者仅仅是将静态的资源通过输出流输出到客户端,而前者需要加载servlet这个类进而按照JavaEE的规范执行servletservice方法。所以在Processor类当中需要请求的URL加载Servlet的类。

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那么会抛出ClassNotFoundException,如果找到所请求的Servlet并且load成功那么下面要做的就是执行其service方法了。

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

在这里Tomcat使用了facade patterns(外观模式?门面模式?) 这样做的好处是:开发人员只能拿到“假”的ResponseRequest类,从而避免了RequestResponse两个类中的类似parse()sendStaticResource()等共有方法被误用。RequestFacade类如下ResponseFacade类大同小异。

public class RequestFacade implements ServletRequest {

    private ServletRequest request = null;

    public RequestFacade(Request request) {
        this.request = request;
    }

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

    public Enumeration getAttributeNames() {
        return request.getAttributeNames();
    }

    public String getRealPath(String path) {
        return request.getRealPath(path);
    }

    public RequestDispatcher getRequestDispatcher(String path) {
        return request.getRequestDispatcher(path);
    }

    public boolean isSecure() {
        return request.isSecure();
    }

    public String getCharacterEncoding() {
        return request.getCharacterEncoding();
    }

    public int getContentLength() {
        return request.getContentLength();
    }

    public String getContentType() {
        return request.getContentType();
    }

    public ServletInputStream getInputStream() throws IOException {
        return request.getInputStream();
    }

    public Locale getLocale() {
        return request.getLocale();
    }

    public Enumeration getLocales() {
        return request.getLocales();
    }

    public String getParameter(String name) {
        return request.getParameter(name);
    }

    public Map getParameterMap() {
        return request.getParameterMap();
    }

    public Enumeration getParameterNames() {
        return request.getParameterNames();
    }

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

    public String getProtocol() {
        return request.getProtocol();
    }

    public BufferedReader getReader() throws IOException {
        return request.getReader();
    }

    public String getRemoteAddr() {
        return request.getRemoteAddr();
    }

    public String getRemoteHost() {
        return request.getRemoteHost();
    }

    public String getScheme() {
        return request.getScheme();
    }

    public String getServerName() {
        return request.getServerName();
    }

    public int getServerPort() {
        return request.getServerPort();
    }

    public void removeAttribute(String attribute) {
        request.removeAttribute(attribute);
    }

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

    public void setCharacterEncoding(String encoding)
            throws UnsupportedEncodingException {
        request.setCharacterEncoding(encoding);
    }

}

(文中涉及到的代码可以在这里找到,参考资料《How Tomcat Works》)

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐