"How Tomcat Works" reading notes (1)

This series comes from: http://blog.csdn.net/wangchengsi/article/details/3981861

 

After reading the first three chapters of this book, it is very well written. It can be said that the analysis of tomcat is very thorough. Although the tomcat described in the book is a "simplified version", the content is not too small, and the more code behind it, the more complicated it becomes. In order to deepen the impression, I decided to write reading notes, "a good memory is not as good as a bad writing", maybe it can be convenient for others.

Without further ado, let's get straight to the point:

Chapter One:A Simple Web Server

The first chapter is a very simple web server, the main purpose is to let readers understand the Java web server programming model. In addition, I also talked about some basic knowledge of the Http protocol, such as the format of the HTTP request and response.

The basic web server uses the java.net.ServerSocket class to continuously monitor a specific port. When there is a connection, it returns a Socket object, and then operates on the input and output streams of the Socket object. In fact, this is the basic idea of ​​most servers.

Just paste some code

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); 
     response.sendStaticResource();

其中,Request对象的parse方法,是对输入流的字节进行解析,当然这里只做了最基本的工作,将输入流的内容一字不漏的打印出来。

的确很简单~这里的Request对象实现了Servlet规范中的Request接口,后面我们会看到,如何运用设计模式令Request更加“优雅”。

 

Chapter Two——A Simple Servlet Container

J2EE流行“容器Container”这一说法,所以这一章开始针对Servlet做一些工作。先说说这个“容器”的架构:

像第一章那样,我们使用一个HttpServer类监听端口,然后将得到的inputStream交给一个Request对象进行解析。然后,根据HTTP请求行(Request Line)的内容,判断是静态页面还是Servlet页面,再分别交给StaticResoureProcessor类或者ServletProcessor类进行处理

// 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 { 
           StaticResoureProcessor processor = 
             new StaticResourceProcessor(); 
           processor.process(request, response); 
         }

StaticResoureProcessor类没什么“技术含量”,只是利用Response类,单纯地将静态页面的内容从html文件中读出来原样返回给客户端(说白了是写到Socket的输出流OutputStream中)。

而Request类在这一章实现了javax.servlet.ServletRequest接口,因此不得不实现接口中的一大堆方法。出于简化考虑,现在大部分方法还是空的,返回一个null而已。

ServletProcessor类

比较出彩的是ServletProcessor类,出彩之处在于通过类加载器从class文件中动态载入Servlet(这里的Servlet的功能只是简单打印字符串),其源代码据说是从tomcat中copy过来的

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. 
    urls[0] = new URL(null, repository, streamHandler); 
    loader = new URLClassLoader(urls); 
}

 

需要解释一下的是,这里的repository 指的是编译好的类文件的存放路径,也算是tomcat里的一个专门术语了

最后,简单的load进来,调用Servlet的经典方法“service”,把Request和Response传进去,就算大功告成了。

myClass = loader.loadClass(servletName);

servlet = (Servlet) myClass.newInstance();  
servlet.service((ServletRequest) request, (ServletResponse) response);

Facade模式

仔细观察上面那行代码,我们将request对象向上转型为ServletRequest,交给service方法。但如果哪个Servlet的开发者清楚tomcat的内部设计,那么他就可以在service方法中将request向下“还原为”Request对象。Request类提供了许多tomcat内部使用的功能,譬如解析http流。如果让外人乱用一气,后果很严重。为了避免这个问题,tomcat采用了Facade设计模式,即通过一个RequestFacade类(实现了ServletRequest接口),替换Request类传递给service方法。而在RequestFacade内部,保存我们的request对象,外人只能通过调用RequestFacade实现了的ServletRequest接口的方法,间接的使用Request的“部分功能”,不能调用Request的一些内部方法,从而优雅的解决了上述问题。

public class RequestFacade implements ServletRequest {  
   private ServleLRequest 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(); 
   }

至此,一个简单的Servlet容器就造出来了。

 

============================================================================

StaticResourceProcessor  

 

ServletRrocessor:

URI -》 servletName -》 URLClassLoader.loadClass(servletName)获取Servlet类myClass

   -》 myClass.newInstance()获取Servlet对象myServlet

   -》 myServlet.service((ServletRequest)request, (ServletResponse)response);  

JAVA类加载机制

URLClassLoader使用方法和解析

ClassLoader 与 URLClassLoader 的用法

Tomcat加载servlet类文件原理分析

Tomcat工作原理分析

 

 ============================================================================

Servlet容器处理Servlet请求的简单流程图如下图所示:



 

 

package com.flyer.tomcat.first;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 一个简单的http服务器
 * <p />
 * 
 * @author Administrator
 */
public class HttpServer {
    
    private static Logger logger = LoggerFactory.getLogger(HttpServer.class);

    private final static String SHUTDOWN_COMMAND = "/shutdown";
    
    public final static String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot";

    private boolean shutdown = false;

    public static void main(String[] args) {
        logger.info("server start");
        HttpServer server = new HttpServer();
        try {
            server.await();
        } catch (IOException e) {
            logger.error("io exception",e);
        }
    }

    private void await() throws IOException {

        ServerSocket serverSocket = new ServerSocket(9090,1,InetAddress.getByName("127.0.0.1"));
        InputStream input = null;
        OutputStream output = null;
        while (!shutdown) {
            Socket socket = null;
            try {
                socket = serverSocket.accept();
                input = socket.getInputStream();
                output = socket.getOutputStream();
                Request request = new Request(input);
                request.parse();
                Response response = new Response(output);
                response.setRequest(request);
                
                if(request.getUri().startsWith("/servlet")){
                    ServletProcessor servletProcessor = new ServletProcessor();
                    servletProcessor.process(request, response);
                }else{
                    StaticResourceProcessor processor = new StaticResourceProcessor();
                    processor.process(request, response);
                }
                
                
                socket.close();

                shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                
                input.close();
                output.close();
            }
        }

    }

}

 

package com.flyer.tomcat.first;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Servlet请求处理器
 * <p />
 * 
 * @author Administrator
 */
public class ServletProcessor implements Processor {

    private static Logger logger = LoggerFactory.getLogger(ServletProcessor.class);

    // private final static String FILE_PATH = Constants.getClassPath() + File.separator + "com" +
    // File.separator
    // + "flyer" + File.separator + "tomcat" + File.separator + "first" + "" + File.separator;

    @Override
    public void process(HttpServletRequest request, HttpServletResponse response) {
        Request internalRequest = (Request) request;
        String uri = internalRequest.getUri();
        String servletClassName = uri.substring(uri.lastIndexOf("/") + 1);
        URLClassLoader classLoader = null;
        File file = new File(Constants.getClassPath());
        URL url = null;
        try {
            url = file.toURI().toURL();
        } catch (MalformedURLException e) {
            logger.error("convert to url error", e);
        }
        logger.info("url:" + url);
        URL[] urls = new URL[1];
        urls[0] = url;
        classLoader = new URLClassLoader(urls);
        Class<?> myClass = null;
        try {
            myClass = classLoader.loadClass(servletClassName);
        } catch (ClassNotFoundException e) {
            logger.error("class not found", e);
        }
        Servlet servlet = null;
        if (myClass != null) {
            try {
                servlet = (Servlet) myClass.newInstance();
            } catch (InstantiationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            try {
                servlet.service(request, response);
            } catch (ServletException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }

}

 

添加facade类,为的是Request类中的parse方法 (解析inputstream请求为字符串,并从中获取URI) 和getUri方法不被Servlet类访问。



 

 

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=327043912&siteId=291194637