美文网首页
How Tomcat Works读书笔记

How Tomcat Works读书笔记

作者: 想54256 | 来源:发表于2019-10-26 17:47 被阅读0次

    title: How Tomcat Works(基于Tomcat4)
    date: 2019/08/12 15:51


    一、一个简单的web服务器

    基于Java的Http服务器需要两个重要的类java.net.ServerSocketjava.net.Socket

    1.1 HTTP

    Http使用可靠的TCP连接,默认使用TCP/80端口

    1.2 Socket类

    套接字是网络连接的端点。套接字使程序可以从网络中读取数据,可以向网络中写入数据。不同计算机上的两个应用程序可以通过连接发送或接受字节流

    Socket类表示客户端套接字(想要连接到远程服务器应用程序时需要创建的套接字)、ServerSocket类表示服务器套接字(等待客户端的连接请求的套接字),当服务器套接字收到请求后,他会创建一个Socket实例来处理与客户端的通信。

    二、一个简单的Servlet容器

    2.1 Servlet接口

    2.2 应用程序1

    一个功能健全的servlet容器对Http请求需要做以下几件事:

    1. 第一次调用某个servlet时,要载入该servlet类,之后调用它的init()方法。
    // 连接器调用pipline,pipline调用基础阀,基础阀执行wrapper.allocate();
    Servlet servlet = this.loadServlet();
    servlet.init();
    
    1. 针对每个request请求,创建一个javax.servlet.ServletRequestjavax.servlet.ServletResponse对象。(连接器干的)
    2. 调用servlet的service方法(基础阀干的,通过过滤器链传递到servlet)
    3. 调用destory方法并卸载此类

    连接器最终生成的是HttpRequestImpl的对象,由于HttpRequestImpl类中有部分公有方法不想让servlet程序员使用,有两种办法能够解决:

    1. 使用访问修饰符
    2. 使用Faced类

    三、连接器

    连接器主要负责创建HttpServletRequest和HttpServletResponse对象,并将他们作为参数传给servlet的service方法。

    3.1 StringManager类

    该类用来处理Catalina中错误消息的国际化操作。

    Tomcat将错误消息存储到properties文件中,如果所有错误信息储存再一个文件中,会很难维护,所以Tomcat将properties文件划分到不同的包中,每个properties文件使用一个StringManager对象来处理。

    private static Hashtable managers = new Hashtable();
    
    public synchronized static StringManager getManager(String packageName) {
        StringManager mgr = (StringManager)managers.get(packageName);
        if (mgr == null) {
            mgr = new StringManager(packageName);
            managers.put(packageName, mgr);
        }
        return mgr;
    }
    
    properties文件格式

    StringManager会根据该应用程序的服务器的语言环境选择使用哪个文件。

    3.2 应用程序

    连接器需要在请求来的时候从socket中一些值设置给Request对象,这些值包括:URI、查询字符串、参数(body中)、cookie、请求头等信息;由于查询字符串和参数可能巨大,所以一般再连接器中不解析它们,而是在servlet真正用到的时候进行解析的

    连接器解析Http请求主要分为以下5块:

    3.2.1 启动程序

    public final class Bootstrap {
        public static void main(String[] args) {
            // HttpConnector负责接受连接,并调用HttpProcessor.process(Socket socket)方法
            HttpConnector connector = new HttpConnector();
            connector.start();
        }
    }
    

    3.2.2 HttpConnector类

    public class HttpConnector implements Runnable {
    
        boolean stopped;
        private String scheme = "http";
    
        public String getScheme() {
            return scheme;
        }
    
        public void run() {
            ServerSocket serverSocket = null;
            int port = 8080;
            serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
            while (!stopped) {
                // 等待http请求
                Socket socket = serverSocket.accept();
                // 调用连接器的process方法,对请求进行解析
                HttpProcessor processor = new HttpProcessor(this);
                processor.process(socket);
            }
        }
    
        public void start() {
            Thread thread = new Thread(this);
            thread.start();
        }
    }
    

    3.2.3 HttpProcessor类

    public void process(Socket socket) {
        SocketInputStream input = null;
        OutputStream output = null;
        try {
            // 获取socket中的输入流、输出流
            input = new SocketInputStream(socket.getInputStream(), 2048);
            output = socket.getOutputStream();
    
            // 创建Request和Response对象
            request = new HttpRequest(input);
            
            response = new HttpResponse(output);
            response.setRequest(request);
            response.setHeader("Server", "Pyrmont Servlet Container");
    
            // 解析请求
            parseRequest(input, output);
            parseHeaders(input);
    
            // 检查这是对servlet还是静态资源的请求,调用不同的处理程序
            // 对servlet的请求以/servlet/开头
            if (request.getRequestURI().startsWith("/servlet/")) {
                ServletProcessor processor = new ServletProcessor();
                processor.process(request, response);
            } else {
                StaticResourceProcessor processor = new StaticResourceProcessor();
                processor.process(request, response);
            }
    
            // Close the socket
            socket.close();
            // no shutdown for this application
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    

    3.2.3.1 创建HttpRequest对象

    主要包含五部分:

    1. 读取套接字的输入流
    2. 解析请求行


      第一行就是请求行
    3. 解析请求头
    4. 解析cookie
    5. 读取参数

    前面说过读取参数会延迟到servlet第一次获取参数时进行解析,所以在request对象中有一个parseParameters()方法,会在第一次获取参数时进行解析。

    3.2.3.2 创建HttpResponse对象

    获取输出流,转换成PrintWirter对象

    四、Tomcat的默认连接器

    功能:负责创建Request、Response对象,然后调用Container接口的invoke方法。

    由于HttpProcessor将Request对象作为全局变量,所以多线程访问的时候有线程安全问题,所以HttpConnector内部维护一个对象池。

    4.2 Connector接口

    Tomcat的连接器必须实现该接口。

    public interface Connector {
    
        // 返回与之关联的容器
        public Container getContainer();
    
        // 将连接器与某个servlet相关联;当request、response对象创建好了之后调用它的invoke方法
        public void setContainer(Container container);
    
        // 为引入的Http请求创建request对象
        public Request createRequest();
    
        // 创建response对象
        public Response createResponse();
        
        // 初始化连接器
        public void initialize() throws LifecycleException;
    
        ...
    }
    

    注:Tomcat8中没有Connector接口,改为org.apache.catalina.connector.Connector类,使用的是Coyote连接器。

    4.3 HttpConnector类(连接器)

    public final class HttpConnector
            implements Connector, Lifecycle, Runnable {
    

    4.3.1 创建服务器套接字

    HttpConnector的initialize()方法会调用open()方法(私有方法)从服务器套接字工厂得到一个服务器套接字实例。

    4.3.2 维护HttpProcessor实例(具体干活的人)

    上面说了,由于一个HttpProcessor实例只能串行的干活,才能保证线程安全,所以默认连接器维护了一个处理器栈:

    private Stack<HttpProcessor> processors = new Stack<>();
    

    内部的数量由两个变量决定:

    private int maxProcessors = 20;
    
    protected int minProcessors = 5;
    

    在HttpConnector启动(Lifecycle的start方法)的时候会对内部对象进行填充:

    public void start() throws LifecycleException {
    
        // Validate and update our current state
        if (started)
            throw new LifecycleException
                    (sm.getString("httpConnector.alreadyStarted"));
        threadName = "HttpConnector[" + port + "]";
        lifecycle.fireLifecycleEvent(START_EVENT, null);
        started = true;
    
        // Start our background thread
        threadStart();
    
        // 创建指定的**最小**处理器数
        while (curProcessors < minProcessors) {
            if ((maxProcessors > 0) && (curProcessors >= maxProcessors))
                break;
            
            // 创建对象
            HttpProcessor processor = newProcessor();
            // 压入栈
            recycle(processor);
        }
    
    }
    

    4.3.3 提供Http请求服务

    HttpConnector实现了Runable接口,上面的threadStart()方法将其启动:

    public void run() {
        // Loop until we receive a shutdown command
        while (!stopped) {
            Socket socket = null;
            try {
                // 等待http请求
                socket = serverSocket.accept();
                if (connectionTimeout > 0)
                    socket.setSoTimeout(connectionTimeout);
                socket.setTcpNoDelay(tcpNoDelay);
            } catch (IOException e) {
                try {
                    // If reopening fails, exit
                    synchronized (threadSync) {
                        if (started && !stopped)
                            log("accept error: ", e);
                        if (!stopped) {
                            serverSocket.close();
                            serverSocket = open();
                        }
                } ...
                continue;
            }
    
            // 获取一个HttpProcessor对象(可能是从栈中取出,可能是创建的,如果max <= current返回null)
            HttpProcessor processor = createProcessor();
            if (processor == null) {
                try {
                    log(sm.getString("httpConnector.noProcessor"));
                    socket.close();
                } catch (IOException e) {
                    ;
                }
                continue;
            }
            // 传入socket对象,唤醒processor的run方法
            processor.assign(socket);
        }
    
        synchronized (threadSync) {
            threadSync.notifyAll();
        }
    
    }
    

    4.3.4 HttpProcessor类

    public void run() {
        // Process requests until we receive a shutdown signal
        while (!stopped) {
    
            // 获取套接字对象,processor.assign(socket)传入的
            Socket socket = await();
            if (socket == null)
                continue;
    
            // 处理
            try {
                process(socket);
            } catch (Throwable t) {
                log("process.invoke", t);
            }
    
            // 将当前实例压会栈中
            connector.recycle(this);
    
        }
    
        // 告诉 threadstop()我们已经成功关闭了自己
        synchronized (threadSync) {
            threadSync.notifyAll();
        }
    }
    
    private void process(Socket socket) {
        // 表示处理过程是否出错
        boolean ok = true;
        boolean finishResponse = true;
        SocketInputStream input = null;
        OutputStream output = null;
    
        // Construct and initialize the objects we will need
        try {
            input = new SocketInputStream(socket.getInputStream(),
                                            connector.getBufferSize());
        } catch (Exception e) {
            log("process.create", e);
            ok = false;
        }
    
        keepAlive = true;
    
        // 不断的读取输入流,知道HttpProcessor实例终止
        while (!stopped && ok && keepAlive) {
    
            finishResponse = true;
    
            try {
                // request、response对象的初始化工作
                request.setStream(input);
                request.setResponse(response);
                output = socket.getOutputStream();
                response.setStream(output);
                response.setRequest(request);
                ((HttpServletResponse) response.getResponse()).setHeader
                    ("Server", SERVER_INFO);
            } catch (Exception e) {
                log("process.create", e);
                ok = false;
            }
    
    
            // Parse the incoming request
            try {
                if (ok) {
    
                    // 解析连接,取出internet地址赋值给Request对象
                    parseConnection(socket);
                    // 解析各种东西,参见第三章
                    parseRequest(input, output);
                    if (!request.getRequest().getProtocol()
                        .startsWith("HTTP/0"))
                        // 解析请求头
                        parseHeaders(input);
                    if (http11) {
                        // Sending a request acknowledge back to the client if
                        // requested.
                        ackRequest(output);
                        // If the protocol is HTTP/1.1, chunking is allowed.
                        if (connector.isChunkingAllowed())
                            response.setAllowChunking(true);
                    }
    
                }
            } catch (EOFException e) {
                // It's very likely to be a socket disconnect on either the
                // client or the server
                ok = false;
                finishResponse = false;
            } catch (ServletException e) {
                ok = false;
                try {
                    ((HttpServletResponse) response.getResponse())
                        .sendError(HttpServletResponse.SC_BAD_REQUEST);
                } catch (Exception f) {
                    ;
                }
            } catch (InterruptedIOException e) {
                if (debug > 1) {
                    try {
                        log("process.parse", e);
                        ((HttpServletResponse) response.getResponse())
                            .sendError(HttpServletResponse.SC_BAD_REQUEST);
                    } catch (Exception f) {
                        ;
                    }
                }
                ok = false;
            } catch (Exception e) {
                try {
                    log("process.parse", e);
                    ((HttpServletResponse) response.getResponse()).sendError
                        (HttpServletResponse.SC_BAD_REQUEST);
                } catch (Exception f) {
                    ;
                }
                ok = false;
            }
    
            // Ask our Container to process this request
            try {
                ((HttpServletResponse) response).setHeader
                    ("Date", FastHttpDateFormat.getCurrentDate());
                if (ok) {
                    // *** 获取容器执行invoke方法,最终到达servlet.service() ***
                    connector.getContainer().invoke(request, response);
                }
            } catch (ServletException e) {
                log("process.invoke", e);
                try {
                    ((HttpServletResponse) response.getResponse()).sendError
                        (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                } catch (Exception f) {
                    ;
                }
                ok = false;
            } catch (InterruptedIOException e) {
                ok = false;
            } catch (Throwable e) {
                log("process.invoke", e);
                try {
                    ((HttpServletResponse) response.getResponse()).sendError
                        (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                } catch (Exception f) {
                    ;
                }
                ok = false;
            }
    
            // 直到finishResponse为true表示处理结束(包括循环读取请求头,调用service方法等),将结果发送给客户端。
            if (finishResponse) {
                try {
                    response.finishResponse();
                } catch (IOException e) {
                    ok = false;
                } catch (Throwable e) {
                    log("process.invoke", e);
                    ok = false;
                }
                try {
                    request.finishRequest();
                } catch (IOException e) {
                    ok = false;
                } catch (Throwable e) {
                    log("process.invoke", e);
                    ok = false;
                }
                try {
                    if (output != null)
                        output.flush();
                } catch (IOException e) {
                    ok = false;
                }
            }
    
            // We have to check if the connection closure has been requested
            // by the application or the response stream (in case of HTTP/1.0
            // and keep-alive).
            if ( "close".equals(response.getHeader("Connection")) ) {
                keepAlive = false;
            }
    
            // End of request processing
            status = Constants.PROCESSOR_IDLE;
    
            // Recycling the request and the response objects
            request.recycle();
            response.recycle();
    
        }
    
        try {
            shutdownInput(input);
            socket.close();
        } catch (IOException e) {
            ;
        } catch (Throwable e) {
            log("process.invoke", e);
        }
        socket = null;
    
    }
    

    五、Servlet容器

    servlet容器是用来处理请求servlet资源,并为客户端填充response对象的模块。

    Tomcat中有下面4中容器:

    • Engine:解析请求,分配到适当的虚拟主机
    • Host:虚拟主机;运行多个Web应用(一个Context代表一个Web应用),并负责安装、展开、启动和结束每个Web应用。
    • Context:代表在特定虚拟主机上运行的一个Web应用
    • Wrapper:表示一个独立的servlet

    它们所有的实现类都继承ContainerBase抽象类。

    https://www.cnblogs.com/kismetv/p/7228274.html#title3-1

    5.1 Container接口

    public interface Container {
    
        // 添加子容器;Host容器下只能添加Context容器。。。
        void addChild(Container child);
    
        // 移除子容器
        void removeChild(Container child);
    
        // 根据名称查找子容器
        public Container findChild(String name);
    
        // 查找子容器集合
        public Container[] findChildren();
    
        // 其它组件的get/set方法,包括:载入器(Loader)、记录器(Logger)、Session管理器(Manager)、领域(Realm)、资源(Resource)
    
        容器是Tomcat的核心,所以才将所有组件都与容器连接起来,而且通过Lifecyle接口,使我们可以只启动容器组件就可以了(他帮我们启动其它组件)
    }
    

    注:这是组合模式的一种使用

    5.2 管道任务

    当连接器调用容器的invoke()方法后,容器会调用pipline的invoke()方法(pipline是管理阀的容器,类似FilterChain;阀表示具体的执行任务),pipline会调用ValveContext的invokeNext方法,当所有阀全都调用完成之后就会调用基础阀的invoke方法。

    看下StandardWrapperValve的invoke方法的部分代码:

    StandardWrapper wrapper = (StandardWrapper) getContainer();
    
    ServletRequest sreq = request.getRequest();
    ServletResponse sres = response.getResponse();
    
    // 如果是第一次的话,这个方法里面调用了servlet的init方法
    Servlet servlet = wrapper.allocate();;
    
    // 构造Filter责任链
    ApplicationFilterChain filterChain = createFilterChain(request, servlet);
    
    // 执行责任链和servlet的service方法
    filterChain.doFilter(sreq, sres);
    

    5.3 Wrapper接口

    Wrapper的实现类主要负责管理其基础的servlet类的生命周期

    // 载入并加载servlet,在它的子类StandardWrapper中直接调用的loadServlet方法
    void load() throws ServletException;
    
    // 该方法会返回已加载的servlet类,还要考虑它是否实现了SingleThreadModel
    Servlet allocate() throws ServletException;
    

    六、生命周期

    Catalina包含很多组件,当Catalina启动时,这些组件也会一起启动,关闭时也会一起关闭。

    通过实现Lifecyle接口就可以达到统一启动/关闭这些组件的效果。

    Lifecycle可以触发下面6个事件,实现LifecycleListener的类可以通过addLifecycleListener()注册到相应的生命周期对象上。

    public interface Lifecycle {
    
        // 添加事件监听器
        void addLifecycleListener(LifecycleListener listener);
    
        /**
         * The LifecycleEvent type for the "component start" event.
         */
        public static final String START_EVENT = "start";
    
    
        /**
         * The LifecycleEvent type for the "component before start" event.
         */
        public static final String BEFORE_START_EVENT = "before_start";
    
    
        /**
         * The LifecycleEvent type for the "component after start" event.
         */
        public static final String AFTER_START_EVENT = "after_start";
    
    
        /**
         * The LifecycleEvent type for the "component stop" event.
         */
        public static final String STOP_EVENT = "stop";
    
    
        /**
         * The LifecycleEvent type for the "component before stop" event.
         */
        public static final String BEFORE_STOP_EVENT = "before_stop";
    
    
        /**
         * The LifecycleEvent type for the "component after stop" event.
         */
        public static final String AFTER_STOP_EVENT = "after_stop";
    

    6.1 Lifecycle接口

    void addLifecycleListener(LifecycleListener listener);
    
    public LifecycleListener[] findLifecycleListeners();
    
    public void removeLifecycleListener(LifecycleListener listener);
    
    // 启动操作
    public void start() throws LifecycleException;
    
    // 关闭操作
    public void stop() throws LifecycleException;
    

    其实也是组合模式的使用

    6.2 LifecycleEvent

    当事件触发的时候传给listener的对象

    6.3 LifecycleListener

    事件触发是的处理程序要放在lifecycleEvent方法中。

    public interface LifecycleListener {
    
        public void lifecycleEvent(LifecycleEvent event);
    }
    

    6.4 LifecycleSupport(工具类)

    public final class LifecycleSupport {
    
        // 构造需要一个生命周期对象
        public LifecycleSupport(Lifecycle lifecycle) {
    
            super();
            this.lifecycle = lifecycle;
    
        }
    
        private Lifecycle lifecycle = null;
    
        // 监听器集合
        private LifecycleListener listeners[] = new LifecycleListener[0];
    
        // 当调用生命周期的addLifecycleListener方法时,大多数代码的实现直接调用的LifecycleSupport的这个方法
        public void addLifecycleListener(LifecycleListener listener) {
    
          synchronized (listeners) {
              LifecycleListener results[] =
                new LifecycleListener[listeners.length + 1];
              for (int i = 0; i < listeners.length; i++)
                  results[i] = listeners[i];
              results[listeners.length] = listener;
              listeners = results;
          }
    
        }
    
    
        public LifecycleListener[] findLifecycleListeners() {
    
            return listeners;
    
        }
    
        // 触发事件,type是上面Lifecyle中定义的那6个常量,data是要传给listener的数据
        public void fireLifecycleEvent(String type, Object data) {
    
            LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
            LifecycleListener interested[] = null;
            synchronized (listeners) {
                interested = (LifecycleListener[]) listeners.clone();
            }
            for (int i = 0; i < interested.length; i++)
                interested[i].lifecycleEvent(event);
    
        }
    
        public void removeLifecycleListener(LifecycleListener listener) {
    
            synchronized (listeners) {
                int n = -1;
                for (int i = 0; i < listeners.length; i++) {
                    if (listeners[i] == listener) {
                        n = i;
                        break;
                    }
                }
                if (n < 0)
                    return;
                LifecycleListener results[] =
                  new LifecycleListener[listeners.length - 1];
                int j = 0;
                for (int i = 0; i < listeners.length; i++) {
                    if (i != n)
                        results[j++] = listeners[i];
                }
                listeners = results;
            }
    
        }
    }
    

    6.5 应用程序

    能体现Lifecyle的就是启动类中的代码了:

    public final class Bootstrap {
        public static void main(String[] args) {
            Connector connector = new HttpConnector();
            Wrapper wrapper1 = new SimpleWrapper();
            wrapper1.setName("Primitive");
            wrapper1.setServletClass("PrimitiveServlet");
            Wrapper wrapper2 = new SimpleWrapper();
            wrapper2.setName("Modern");
            wrapper2.setServletClass("ModernServlet");
    
            Context context = new SimpleContext();
            context.addChild(wrapper1);
            context.addChild(wrapper2);
    
            Mapper mapper = new SimpleContextMapper();
            mapper.setProtocol("http");
            // 将监听器绑定到context容器中
            LifecycleListener listener = new SimpleContextLifecycleListener();
            ((Lifecycle) context).addLifecycleListener(listener);
            context.addMapper(mapper);
            Loader loader = new SimpleLoader();
            context.setLoader(loader);
            // context.addServletMapping(pattern, name);
            context.addServletMapping("/Primitive", "Primitive");
            context.addServletMapping("/Modern", "Modern");
            connector.setContainer(context);
            try {
                // 初始化连接器(创建ServerSocket)
                connector.initialize();
                // 启动连接器(创建HttpProcesser集合)
                ((Lifecycle) connector).start();
                // 启动容器(它又会调用子容器的start()方法)
                ((Lifecycle) context).start();
    
                // make the application wait until we press a key.
                System.in.read();
                ((Lifecycle) context).stop();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    6.6 总结

    通过生命周期就可以采用伪递归的方式让组件做一些开始、收尾工作。

    LifecyleListener就是在启动/关闭过程中埋下的钩子。

    七、日志记录器

    日志记录器是用来记录消息的组件。

    https://wiki.jikexueyuan.com/project/tomcat/logging.html

    7.1 Logger接口

    日志记录器都必须实现这个接口。

    public interface Logger {
    
        // 定义的日志的等级
        public static final int FATAL = Integer.MIN_VALUE;
    
        public static final int ERROR = 1;
    
        public static final int WARNING = 2;
    
        public static final int INFORMATION = 3;
    
        public static final int DEBUG = 4;
    

    7.2 Tomcat的日志记录器

    Tomcat提供了3种日志记录器:FileLogger、SystemOutLogger、SystemErrLogger,它们3个都继承了LoggerBase抽象类。

    7.2.1 LoggerBase类

    实现了所有Logger接口的方法,除了下面这个:

    public abstract void log(String msg);
    

    LoggerBase中有几个重载的log方法,最终都会调用这个方法。

    日志等级是由protected int verbosity = ERROR;变量定义的,默认为Error。

    7.2.2 SystemOutLogger类

    public class SystemOutLogger extends LoggerBase {
        public void log(String msg) {
            System.out.println(msg);
        }
    }
    

    7.2.3 SystemErrLogger类

    public class SystemErrLogger extends LoggerBase {
        public void log(String msg) {
            System.err.println(msg);
        }
    }
    

    7.2.4 FileLogger

    当我知道Tomcat8中没有Logger接口,就不想写了

    八、载入器

    由于整个Tomcat共用一个JVM,如果采用系统类加载器的话所有web应用程序(Context)之间可以互相使用对方的类,违背了下面的规范:

    为部署在单个Tomcat实例中的每个Web应用程序创建一个类加载器。/WEB-INF/classesWeb应用程序目录中的所有解压缩的类和资源,以及Web应用程序/WEB-INF/lib目录下的JAR文件中的类和资源,都对此Web应用程序可见,但对其他应用程序不可见。 —— Tomcat7官方文档

    还有另外一个原因是为了提供自动重载功能,当WEB-INF/classes和WEB-INF/lib目录发生变化时,Web应用要重新载入这些类。

    Tomcat要自定义类加载器原因主要是以下3条:

    1. 在载入类中指定某些规则
    2. 缓存已经载入的类
    3. 实现类的预载入

    8.1 类加载器

    JVM使用了3种类加载器来载入需要的类,分别是引导类加载器(Bootstrap ClassLoader)、扩展类加载器(Extension ClassLoader)、系统类加载器(System ClassLoader)。

    3种类加载器是父子继承关系,引导类加载器在最上层,系统类加载器在最下层。

    引导类加载器(本地代码编写):引导启动Java虚拟机。用于加载JVM需要的类,以及所有Java核心类(例如java.lang,java.io包下的类);它会在rt.jar、i18n.jar中搜索要载入的类。

    扩展类加载器:负责载入标准扩展目录中的类。标准扩展目录是/jdk/jre/lib/ext

    系统类加载器:它会搜索在环境变量CLASSPATH中指明的路径和文件。

    类载入过程(双亲委托机制):要载入一个类的时候,会先交给系统类加载器,系统类加载器交给扩展类加载器,扩展类加载器又会交给引导类加载器,如果引导类加载器无法加载就交给扩展类加载器,扩展类加载器无法加载就交给系统类加载器,系统类加载器无法加载就抛出ClassNotFoundException异常。

    为啥要执行这样的循环过程呢?

    如果我们自定义一个java.lang.Object类,假设他能被载入,由于JVM是信任java.lang.Object类的,安全管理器就不会监视这个类的活动,这是非常危险的。

    采用双亲委托机制,就会将java.lang.Object类的加载交给引导类加载器,而引导类加载器会在rt.jar、i18n.jar中去寻找这个类进行加载,我们自定义的类并不会被加载。

    package java.lang;
    
    public class Object {
        public static void main(String[] args) {
            System.out.println(123);
        }
    }
    
    // 证明自定义Object类并没有加载进去
    错误: 在类 java.lang.Object 中找不到 main 方法, 请将 main 方法定义为:
       public static void main(String[] args)
    否则 JavaFX 应用程序类必须扩展javafx.application.Application
    

    8.2 Loader接口

    仓库:表示类载入器会在哪里搜索要载入的类

    资源:一个类载入器中的DirContext对象(代表着一个web应用的目录吗??走流程的时候看一下)

    public interface Loader {
        
        // 返回类加载器
        ClassLoader getClassLoader();
    
        Context getContext();
    
        // 这个方法会在容器启动(start方法)的时候调用,setContext(this)
        void setContext(Context var1);
    
        boolean getDelegate();
    
        // 是否委托给父类载入器
        void setDelegate(boolean var1);
    
        boolean getReloadable();
    
        void setReloadable(boolean var1);
    
        void addPropertyChangeListener(PropertyChangeListener var1);
    
        boolean modified();
    
        void removePropertyChangeListener(PropertyChangeListener var1);
    }
    

    Loader的实现类

    public class WebappLoader
        implements Lifecycle, Loader, PropertyChangeListener, Runnable {
    
        // ClassLoader的子类
        private WebappClassLoader classLoader = null;
    

    8.3 Reloader接口

    public interface Reloader {
    
    
        /**
         * 设置仓库路径(在哪里搜索要载入的类)
         */
        public void addRepository(String repository);
    
    
        /**
         * Return a String array of the current repositories for this class
         * loader.  If there are no repositories, a zero-length array is
         * returned.
         */
        public String[] findRepositories();
    
    
        /**
         * 是否修改了一个或多个类或资源
         */
        public boolean modified();
    }
    

    8.4 WebappLoader

    public class WebappLoader
        implements Lifecycle, Loader, PropertyChangeListener, Runnable {
    
        // ClassLoader的子类
        private WebappClassLoader classLoader = null;
    

    它负责载入Web应用程序中所使用到的类,支持自动重载功能(通过Runable的run方法不断轮询其类加载器的modified()实现)

    当调用该类的start()方法时会做以下几项工作:

    8.4.1 创建类载入器

    private WebappClassLoader createClassLoader()
            throws Exception {
            
            // loaderClass可以通过setLoaderClass方法改变
            Class clazz = Class.forName(loaderClass);
    

    8.4.2 设置仓库

    // 伪代码
    private void setRepositories() {
    
        String classesPath = "/WEB-INF/classes";
        // addRepository的重载方法,第二个参数传入的是 new File(应用/WEB-INF/classes)对象
        classLoader.addRepository(classesPath + "/", classRepository);
    
        String libPath = "/WEB-INF/lib";
        classLoader.setJarPath(libPath);
    

    8.4.3 设置类路径

    为jasper JSP编译器设置一个字符串形式的属性来表明类路径信息。

    8.4.4 设置访问权限

    如果运行时使用了安全管理器,setPermissions()方法会为泪在如期设置访问目录的权限,例如:设置只能访问WEB-INF/classes和WEB-INF/lib目录。

    8.4.5 开启新线程执行类的重新载入

    public void run() {
    
        // Loop until the termination semaphore is set
        while (!threadDone) {
    
            // Wait for our check interval
            threadSleep();
    
            if (!started)
                break;
    
            try {
                // Perform our modification check
                if (!classLoader.modified())
                    continue;
            } catch (Exception e) {
                log(sm.getString("webappLoader.failModifiedCheck"), e);
                continue;
            }
    
            // 重新加载;最终调用的是context.reload()方法
            notifyContext();
            // 为啥这里要退出呢??会不会在上面reload的过程中重新启动了一个WebappLoader呢?
            break;
    
        }
    }
    

    Tomcat5看书 P119

    8.5 WebappClassLoader类

    public class WebappClassLoader
        extends URLClassLoader
        implements Reloader, Lifecycle {
    

    继承自URLClassLoader,实现了Reloader接口,支持重载。

    它会缓存之前已经加载过的类来提高性能,它还会缓存加载失败的类的名字,当再次请求的时候就会直接抛出ClassNotFoundException异常,而不会在指定的仓库或jar包中搜索需要载入的类。

    为了安全性,不让这个加载器加载某些类,类的名称存储在一个字符串数组变量triggerd中:

    private static final String[] triggers = {
        "javax.servlet.Servlet"                     // Servlet API
    };
    

    此外,某些特殊包下的类也是不允许载入的:

    private static final String[] packageTriggers = {
        "javax",                                     // Java extensions
        "org.xml.sax",                               // SAX 1 & 2
        "org.w3c.dom",                               // DOM 1 & 2
        "org.apache.xerces",                         // Xerces 1 & 2
        "org.apache.xalan"                           // Xalan
    };
    

    8.5.1 类缓存

    java.lang.ClassLoader类会维护一个Vector<Class<?>>对象,保存已经载入的类,防止这些类在不使用的时候被垃圾回收了。

    每个由WebappClassLoader载入的类(从/WEB-INF/classes,/WEB-INF/lib等目录下载入的类),都视为“资源”,“资源”使用ResourceEntry进行表示:

    public class ResourceEntry {
    
        /**
         * The "last modified" time of the origin file at the time this class
         * was loaded, in milliseconds since the epoch.
         */
        public long lastModified = -1;
    
    
        /**
         * Binary content of the resource.
         */
        public byte[] binaryContent = null;
    
    
        /**
         * Loaded class.
         */
        public Class loadedClass = null;
    
    
        /**
         * URL source from where the object was loaded.
         */
        public URL source = null;
    
    
        /**
         * URL of the codebase from where the object was loaded.
         */
        public URL codeBase = null;
    
    
        /**
         * Manifest (if the resource was loaded from a JAR).
         */
        public Manifest manifest = null;
    
    
        /**
         * Certificates (if the resource was loaded from a JAR).
         */
        public Certificate[] certificates = null;
    
    }
    

    所有已经缓存的类会存储在一个HashMap中:

    protected HashMap<String, ResourceEntry> resourceEntries = new HashMap<>();
    

    8.5.2 载入类

    1. 先检查本地缓存
    2. 本地缓存没有,则检查上一层缓存(调用ClassLoader.findLoadedClass())
    3. 如果两个缓存中没有,则使用系统类加载器加载,防止Web应用程序的类覆盖J2EE的类(原因前面讲了)
    4. 如果使用了安全管理器(与8.4.4对应),则检查是否允许载入该类,如果不允许则抛出ClassNotFoundException
    5. 如果设置了Delegate属性或待载入的类属于包触发器(packageTriggers)的包名,则调用父类加载器来载入相关类,如果父类加载器为null,则使用系统类加载器。
    6. 从当前仓库载入相关类
    7. 如果当前仓库没有需要的类,且标志位delegate关闭,则使用父类加载器;如果父类加载器为null,则使用系统类加载器
    8. 如果还是没找到需要的类,则抛出ClassNotFoundException异常。

    九、Session管理

    Catalina通过一个称为Session管理器的组件来管理建立的Session对象,该组件必须实现Manager接口。Session管理器必须与一个Context容器相关联。

    我们可以通过request.getSession()来获取Session:

    // HttpRequestBase.java
    public HttpSession getSession() {
        return (getSession(true));
    }
    
    public HttpSession getSession(boolean create) {
        if( System.getSecurityManager() != null ) {
            PrivilegedGetSession dp = new PrivilegedGetSession(create);
            return (HttpSession)AccessController.doPrivileged(dp);
        }
        return doGetSession(create);
    }
    
    private HttpSession doGetSession(boolean create) {
        // There cannot be a session if no context has been assigned yet
        if (context == null)
            return (null);
    
        // Return the current session if it exists and is valid
        if ((session != null) && !session.isValid())
            session = null;
        if (session != null)
            return (session.getSession());
    
    
        // Return the requested session if it exists and is valid
        Manager manager = null;
        if (context != null)
            // 从context中获取session管理器
            manager = context.getManager();
    
        if (manager == null)
            return (null);      // Sessions are not supported
    
        if (requestedSessionId != null) {
            try {
                // 如果sessionId不为空,那么从管理器中寻找他
                session = manager.findSession(requestedSessionId);
            } catch (IOException e) {
                session = null;
            }
            if ((session != null) && !session.isValid())
                session = null;
            if (session != null) {
                return (session.getSession());
            }
        }
    
        // Create a new session if requested and the response is not committed
        if (!create)
            return (null);
        if ((context != null) && (response != null) &&
            context.getCookies() &&
            response.getResponse().isCommitted()) {
            throw new IllegalStateException
                (sm.getString("httpRequestBase.createCommitted"));
            }
    
            // 通过管理器创建一个session
            session = manager.createSession();
            if (session != null)
                return (session.getSession());
            else
                return (null);
    
        }
    

    9.1 Session对象

    9.1.1 Session接口

    public interface Session {
    
        // 获取该session的标识符
        public String getId();
    
        public void setId(String id);
    
        // 获取session管理器
        public Manager getManager();
    
    
        // 设置session管理器
        public void setManager(Manager manager);
    
        // 返回客户端上次发送与此会话关联的请求的时间(可以通过该返回值判断session对象的有效性)
        public long getLastAccessedTime();
    
        // 设置session的有效性
        public void setValid(boolean isValid);
    
        // 当访问一个session实例的时候,session管理器会调用该方法修改session对象的最终访问时间
        public void access();
    
        // 将session设置为过期
        public void expire();
    
        // 获取一个被外观类包装的HttpSession类
        public HttpSession getSession();
    
    }
    

    9.1.2 StandardSession类

    class StandardSession
            implements HttpSession, Session, Serializable {
    
        // 构造函数接收的是session管理器对象
        public StandardSession(Manager manager) {
            super();
            this.manager = manager;
            if (manager instanceof ManagerBase)
                this.debug = ((ManagerBase) manager).getDebug();
        }
    
        public HttpSession getSession() {
            if (facade == null)
                facade = new StandardSessionFacade(this);
            return facade;
        }
    
        // 设置session过期
        public void expire() {
            expire(true);
        }
    
        public void expire(boolean notify) {
    
            // Mark this session as "being expired" if needed
            if (expiring)
                return;
            // 将过期标识设置为true,有效标识设置为false
            expiring = true;
            setValid(false);
    
            // 从session管理器移除当前session对象
            if (manager != null)
                manager.remove(this);
    
            // 解绑与当前session相关联的任何对象
            String keys[] = keys(); // 获取session域的所有key值
            for (int i = 0; i < keys.length; i++)
                removeAttribute(keys[i], notify);
    
            // 触发session过期事件
            if (notify) {
                fireSessionEvent(Session.SESSION_DESTROYED_EVENT, null);
            }
    
            // Notify interested application event listeners
            // FIXME - Assumes we call listeners in reverse order
            Context context = (Context) manager.getContainer();
            Object listeners[] = context.getApplicationListeners();
            if (notify && (listeners != null)) {
                HttpSessionEvent event =
                        new HttpSessionEvent(getSession());
                for (int i = 0; i < listeners.length; i++) {
                    int j = (listeners.length - 1) - i;
                    if (!(listeners[j] instanceof HttpSessionListener))
                        continue;
                    HttpSessionListener listener =
                            (HttpSessionListener) listeners[j];
                    try {
                        // 触发容器事件
                        fireContainerEvent(context,
                                "beforeSessionDestroyed",
                                listener);
                        listener.sessionDestroyed(event);
                        fireContainerEvent(context,
                                "afterSessionDestroyed",
                                listener);
                    } catch (Throwable t) {
                        try {
                            fireContainerEvent(context,
                                    "afterSessionDestroyed",
                                    listener);
                        } catch (Exception e) {
                            ;
                        }
                        // FIXME - should we do anything besides log these?
                        log(sm.getString("standardSession.sessionEvent"), t);
                    }
                }
            }
    
            // We have completed expire of this session
            expiring = false;
            if ((manager != null) && (manager instanceof ManagerBase)) {
                recycle();
            }
    
        }
    

    9.2 Manager

    Session管理器负责管理Session对象(Session对象的创建、摧毁)。

    ManagerBase抽象类对Manager接口进行了部分实现,它有2个直接子类:

    • StandardManager:将session对象储存在内存中,当Catalina关闭时,他会将Session对象存储到一个文件中(Lifecycle的stop方法中),重启后,又会将这些Session对象重新载入内存中。

    • PersistentManagerBase:它也是一个抽象类,用于将session对象存储到辅助存储器中。

    public interface Manager {
        
        // 将Manager与容器相关联
        public void setContainer(Container container);
    
        // 创建一个session实例
        public Session createSession();
    
        // 将session实例添加到Session池中
        public void add(Session session);
    
        // 将session实例从Session池中移除
        public void remove(Session session);
    
        // 设置此管理器创建的session对象的默认最长存活时间
        public void setMaxInactiveInterval(int interval);
    
        // 持久化到储存器中
        public void unload() throws IOException;
        
        // 从储存器中拿出来
        public void load() throws ClassNotFoundException, IOException;
    
    

    9.2.2 ManagerBase类

    Context容器中所有活动的Session对象都存储在一个名为sessions的HashMap变量中:

    protected HashMap sessions = new HashMap();
    
    public void add(Session session) {
        synchronized (sessions) {
            sessions.put(session.getId(), session);
        }
    }
    

    9.2.3 StandardManager类

    实现了Lifecyle接口,通过它对Session对象进行load和unload;会生成一个SESSION.ser的文件中。

    session管理器还要负责销毁那些失效的Session对象:

    public void run() {
        while (!threadDone) {
            threadSleep();
            processExpires();
        }
    }
    

    9.2.4 PersistentManagerBase类

    // 使用stroe代表辅助储存器
    private Store store = null;
    

    本章剩下的看书把。

    十一、StandardWrapper

    11.1 方法调用序列

    image

    1、连接器创建request和response对象

    2、连接器调用StandardContext实例的invoke()方法

    3、StandardContext实例的invoke()方法会调用其管道对象(Pipline)的invoke()方法。最终会调用管道对象中的基础阀StandardContextValve实例的invoke()方法。

    4、StandardContextValve实例的invoke()方法会获取相应的Wrapper实例处理Http请求,调用Wrapper实例的invoke方法。

    5、同上,StandardWrapper实例的invoke()方法调用其管道对象的invoke()方法。

    6、同上,调用基础阀StandardWrapperValve实例的invoke()方法,invoke方法会调用Wrapper实例的allocate()方法获取servlet实例。

    7、allocate()调用load()方法载入相应的servlet类,如果已经载入,无需重新载入。

    8、load()方法会调用servlet实例的init()方法

    9、StandardWrapper实例调用servlet实例的service()方法。

    11.3 StandardWrapper

    StandardWrapper对象的主要任务是载入(loader.load())它所代表的servlet类,并进行实例化(clazz.newInstance())。

    11.3.1 分配servlet实例(Wrapper的allocate()方法)

    public Servlet allocate() throws ServletException {
    
        // SingleThreadedModel现在已经废弃,就不介绍了
        if (!singleThreadModel) {
    
            // 双重检查锁,检查servlet之前是否加载过
            if (instance == null) {
                synchronized (this) {
                    if (instance == null) {
                        try {
                            instance = loadServlet();
                        } catch (ServletException e) {
                            throw e;
                        } catch (Throwable e) {
                            throw new ServletException
                                    (sm.getString("standardWrapper.allocate"), e);
                        }
                    }
                }
            }
    
            if (!singleThreadModel) {
                if (debug >= 2)
                    log("  Returning non-STM instance");
                // 将已分配的计数+1
                countAllocated++;
                return (instance);
            }
    
        }
    }
    

    11.3.2 载入Servlet类(Wrapper的load()方法)

    public synchronized void load() throws ServletException {
        instance = loadServlet();
    }
    
    // allocate()和load()方法都调用了该方法
    public synchronized Servlet loadServlet() throws ServletException {
    
        // 如果之前已经加载过了,直接返回
        if (instance != null)
            return instance;
    
        PrintStream out = System.out;
        SystemLogHandler.startCapture();
    
        Servlet servlet = null;
        try {
            // 将servlet类的类名赋值给actualClass
            String actualClass = servletClass;
            // 检查请求的servlet是不是一个jsp页面,如果是的话重新赋值actualClass
            if ((actualClass == null) && (jspFile != null)) {
                Wrapper jspWrapper = (Wrapper)
                        ((Context) getParent()).findChild(Constants.JSP_SERVLET_NAME);
                if (jspWrapper != null)
                    actualClass = jspWrapper.getServletClass();
            }
    
            // 如果它是servlet,但是没有setServletClass()就会报错
            if (actualClass == null) {
                unavailable(null);
                throw new ServletException
                        (sm.getString("standardWrapper.notClass", getName()));
            }
    
            // 获取载入器(如果当前容器没有载入器,就获取它父容器的载入器)
            Loader loader = getLoader();
            if (loader == null) {
                unavailable(null);
                throw new ServletException
                        (sm.getString("standardWrapper.missingLoader", getName()));
            }
    
            // 获取载入器中的类加载器
            ClassLoader classLoader = loader.getClassLoader();
    
            // Catalina提供了一些用于访问servlet容器内部数据专用的servlet类,如果要加载的类是这种专用的servlet(参见19章),则将ClassLoader辅值为当前类的加载器
            if (isContainerProvidedServlet(actualClass)) {
                classLoader = this.getClass().getClassLoader();
                log(sm.getString
                        ("standardWrapper.containerServlet", getName()));
            }
    
            // Load the specified servlet class from the appropriate class loader
            Class classClass = null;
            try {
                if (classLoader != null) {
                    System.out.println("Using classLoader.loadClass");
                    // 使用类加载器加载该类
                    classClass = classLoader.loadClass(actualClass);
                } else {
                    System.out.println("Using forName");
                    classClass = Class.forName(actualClass);
                }
            } catch (ClassNotFoundException e) {
                unavailable(null);
                throw new ServletException
                        (sm.getString("standardWrapper.missingClass", actualClass),
                                e);
            }
            if (classClass == null) {
                unavailable(null);
                throw new ServletException
                        (sm.getString("standardWrapper.missingClass", actualClass));
            }
    
            // Instantiate and initialize an instance of the servlet class itself
            try {
                // 实例化
                servlet = (Servlet) classClass.newInstance();
            } catch (ClassCastException e) {
                unavailable(null);
                // Restore the context ClassLoader
                throw new ServletException
                        (sm.getString("standardWrapper.notServlet", actualClass), e);
            } catch (Throwable e) {
                unavailable(null);
                // Restore the context ClassLoader
                throw new ServletException
                        (sm.getString("standardWrapper.instantiate", actualClass), e);
            }
    
            // 检查该类是否允许载入
            if (!isServletAllowed(servlet)) {
                throw new SecurityException
                        (sm.getString("standardWrapper.privilegedServlet",
                                actualClass));
            }
    
            // 检查该servlet是否实现了ContainerServlet接口(参见19章),如果实现了,将当前Wrapper对象设置进去
            if ((servlet instanceof ContainerServlet) &&
                    isContainerProvidedServlet(actualClass)) {
                System.out.println("calling setWrapper");
                ((ContainerServlet) servlet).setWrapper(this);
                System.out.println("after calling setWrapper");
            }
    
            try {
                // 触发BEFORE_INIT_EVENT事件
                instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT, servlet);
    
                // 初始化servlet
                servlet.init(facade);
    
                // loadOnStartup:此servlet的启动时加载顺序值,负值表示首次调用时加载
                // 如果是jsp页面,调用该servlet的service方法
                if ((loadOnStartup > 0) && (jspFile != null)) {
                    // Invoking jspInit
                    HttpRequestBase req = new HttpRequestBase();
                    HttpResponseBase res = new HttpResponseBase();
                    req.setServletPath(jspFile);
                    req.setQueryString("jsp_precompile=true");
                    servlet.service(req, res);
                }
                // 触发AFTER_INIT_EVENT事件
                instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT, servlet);
            } catch (UnavailableException f) {
                instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
                        servlet, f);
                unavailable(f);
                throw f;
            } catch (ServletException f) {
                instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
                        servlet, f);
                // If the servlet wanted to be unavailable it would have
                // said so, so do not call unavailable(null).
                throw f;
            } catch (Throwable f) {
                instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
                        servlet, f);
                // If the servlet wanted to be unavailable it would have
                // said so, so do not call unavailable(null).
                throw new ServletException
                        (sm.getString("standardWrapper.initException", getName()), f);
            }
    
        } finally {
            String log = SystemLogHandler.stopCapture();
            if (log != null && log.length() > 0) {
                if (getServletContext() != null) {
                    getServletContext().log(log);
                } else {
                    out.println(log);
                }
            }
        }
        return servlet;
    
    }
    

    11.3.3 ServletConfig对象

    StandardWrapper本身就是ServletConfig对象。

    上面loadServlet()方法中加载完servlet之后对其进行初始化,init()方法需要传入一个ServletConfig对象:

    // 初始化servlet;因为不信任servlet程序员所以使用一个门面进行包装一下
    servlet.init(facade);
    
    public interface ServletConfig {
        String getServletName();
    
        ServletContext getServletContext();
    
        String getInitParameter(String var1);
    
        Enumeration getInitParameterNames();
    }
    
    public final class StandardWrapper
            extends ContainerBase
            implements ServletConfig, Wrapper {
    
        public String getServletName() {
            return name;
        }
    
        public ServletContext getServletContext() {
            if (parent == null)
                return (null);
            else if (!(parent instanceof Context))
                return (null);
            else
                return (((Context) parent).getServletContext());
        }
    
        private HashMap parameters = new HashMap();
    
        public String getInitParameter(String name) {
            return (findInitParameter(name));
        }
    
        public String findInitParameter(String name) {
            synchronized (parameters) {
                return ((String) parameters.get(name));
            }
        }
    
        public Enumeration getInitParameterNames() {
            synchronized (parameters) {
                return (new Enumerator(parameters.keySet()));
            }
        }
    

    11.3.4 Servlet容器的父子关系

    Wrapper实例代表一个servlet实例,不能再有子容器,所以调用它的addChildren()方法会报错。

    11.5 StandardWrapperValve(基础阀)

    它主要要干以下两个操作:

    1. 执行与该servlet实例关联的全部过滤器(Filter)
    2. 调用servlet实例的service()方法

    它的invoke方法:

    public void invoke(Request request, Response response,
                           ValveContext valveContext)
                throws IOException, ServletException {
            ...
    
            // Allocate a servlet instance to process this request
            try {
                if (!unavailable) {
                    // 获取servlet实例
                    servlet = wrapper.allocate();
                }
            } catch (ServletException e) {
                log(sm.getString("standardWrapper.allocateException",
                        wrapper.getName()), e);
                throwable = e;
                exception(request, response, e);
                servlet = null;
            } catch (Throwable e) {
                log(sm.getString("standardWrapper.allocateException",
                        wrapper.getName()), e);
                throwable = e;
                exception(request, response, e);
                servlet = null;
            }
    
            // Acknowlege the request
            try {
                response.sendAcknowledgement();
            } catch (IOException e) {
                sreq.removeAttribute(Globals.JSP_FILE_ATTR);
                log(sm.getString("standardWrapper.acknowledgeException",
                        wrapper.getName()), e);
                throwable = e;
                exception(request, response, e);
            } catch (Throwable e) {
                log(sm.getString("standardWrapper.acknowledgeException",
                        wrapper.getName()), e);
                throwable = e;
                exception(request, response, e);
                servlet = null;
            }
    
            // 创建过滤器链
            ApplicationFilterChain filterChain = createFilterChain(request, servlet);
    
            // Call the filter chain for this request
            // NOTE: This also calls the servlet's service() method
            try {
                String jspFile = wrapper.getJspFile();
                if (jspFile != null)
                    sreq.setAttribute(Globals.JSP_FILE_ATTR, jspFile);
                else
                    sreq.removeAttribute(Globals.JSP_FILE_ATTR);
                if ((servlet != null) && (filterChain != null)) {
    
                    // 调用过滤器链
                    filterChain.doFilter(sreq, sres);
                }
                sreq.removeAttribute(Globals.JSP_FILE_ATTR);
            } catch (IOException e) {
                sreq.removeAttribute(Globals.JSP_FILE_ATTR);
                log(sm.getString("standardWrapper.serviceException",
                        wrapper.getName()), e);
                throwable = e;
                exception(request, response, e);
            } catch (UnavailableException e) {
                sreq.removeAttribute(Globals.JSP_FILE_ATTR);
                log(sm.getString("standardWrapper.serviceException",
                        wrapper.getName()), e);
                //            throwable = e;
                //            exception(request, response, e);
                wrapper.unavailable(e);
                long available = wrapper.getAvailable();
                if ((available > 0L) && (available < Long.MAX_VALUE))
                    hres.setDateHeader("Retry-After", available);
                hres.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
                        sm.getString("standardWrapper.isUnavailable",
                                wrapper.getName()));
                // Do not save exception in 'throwable', because we
                // do not want to do exception(request, response, e) processing
            } catch (ServletException e) {
                sreq.removeAttribute(Globals.JSP_FILE_ATTR);
                log(sm.getString("standardWrapper.serviceException",
                        wrapper.getName()), e);
                throwable = e;
                exception(request, response, e);
            } catch (Throwable e) {
                sreq.removeAttribute(Globals.JSP_FILE_ATTR);
                log(sm.getString("standardWrapper.serviceException",
                        wrapper.getName()), e);
                throwable = e;
                exception(request, response, e);
            }
    
            // Release the filter chain (if any) for this request
            try {
                if (filterChain != null)
                    // 释放过滤器链
                    filterChain.release();
            } catch (Throwable e) {
                log(sm.getString("standardWrapper.releaseFilters",
                        wrapper.getName()), e);
                if (throwable == null) {
                    throwable = e;
                    exception(request, response, e);
                }
            }
    
            // Deallocate the allocated servlet instance
            try {
                if (servlet != null) {
                    // 释放对该servlet的“引用计数”
                    wrapper.deallocate(servlet);
                }
            } catch (Throwable e) {
                log(sm.getString("standardWrapper.deallocateException",
                        wrapper.getName()), e);
                if (throwable == null) {
                    throwable = e;
                    exception(request, response, e);
                }
            }
    
            // If this servlet has been marked permanently unavailable,
            // unload it and release this instance
            try {
                if ((servlet != null) &&
                        (wrapper.getAvailable() == Long.MAX_VALUE)) {
                    // 如果该servlet类被标记为永久不可用,则调用Wrapper.unload()方法
                    wrapper.unload();
                }
            } catch (Throwable e) {
                log(sm.getString("standardWrapper.unloadException",
                        wrapper.getName()), e);
                if (throwable == null) {
                    throwable = e;
                    exception(request, response, e);
                }
            }
    
        }
    

    11.6 FilterDef

    // FilterDef类中的每个属性表示在定义filter元素时声明的子元素(web.xml)。parameters中保存的是初始化过滤器时所需要的参数。
    public final class FilterDef {
    
        /**
         * The description of this filter.
         */
        private String description = null;
    
        /**
         * The display name of this filter.
         */
        private String displayName = null;
    
        /**
         * The fully qualified name of the Java class that implements this filter.
         */
        private String filterClass = null;
    
        /**
         * The name of this filter, which must be unique among the filters
         * defined for a particular web application.
         */
        private String filterName = null;
    
        /**
         * The large icon associated with this filter.
         */
        private String largeIcon = null;
    
        /**
         * The set of initialization parameters for this filter, keyed by
         * parameter name.
         */
        private Map parameters = new HashMap();
    
    
        /**
         * The small icon associated with this filter.
         */
        private String smallIcon = null;
    
    
        // --------------------------------------------------------- Public Methods
    
    
        /**
         * Add an initialization parameter to the set of parameters associated
         * with this filter.
         *
         * @param name The initialization parameter name
         * @param value The initialization parameter value
         */
        public void addInitParameter(String name, String value) {
    
            parameters.put(name, value);
    
        }
    

    11.7 ApplicationFilterConfig类

    final class ApplicationFilterConfig implements FilterConfig {
    
        // context对象表示一个web应用程序
        // filterDef表示一个过滤器的定义
        public ApplicationFilterConfig(Context context, FilterDef filterDef)
            throws ClassCastException, ClassNotFoundException,
                   IllegalAccessException, InstantiationException,
                   ServletException {
    
            super();
            this.context = context;
            setFilterDef(filterDef);
    
            // 尝试从filterDef中获取Filter对象,如果为空则通过反射的形式创建filter对象
            if (filterDef.getFilter() == null) {
                getFilter();
            } else {
                this.filter = filterDef.getFilter();
                getInstanceManager().newInstance(filter);
                initFilter();
            }
        }
    
        // 负责载入并实例化一个过滤器类
        Filter getFilter() throws ClassCastException, ClassNotFoundException,
            IllegalAccessException, InstantiationException, ServletException {
    
            // Return the existing filter instance, if any
            if (this.filter != null)
                return (this.filter);
    
            // 获取过滤器的全类路径
            String filterClass = filterDef.getFilterClass();
            ClassLoader classLoader = null;
            if (filterClass.startsWith("org.apache.catalina."))
                classLoader = this.getClass().getClassLoader();
            else
                classLoader = context.getLoader().getClassLoader();
    
            ClassLoader oldCtxClassLoader =
                Thread.currentThread().getContextClassLoader();
    
            // 使用类加载器加载该类
            Class clazz = classLoader.loadClass(filterClass);
            // 构造+初始化
            this.filter = (Filter) clazz.newInstance();
            filter.init(this);
            return (this.filter);
    
        }
    

    11.9 ApplicationFilterChain类

    Filter的doFilter()方法会接受一个ApplicationFilterChain对象,可以直接调用chain.doFilter()调用下一个过滤器。

    十二、StandardContext类

    Context容器需要其它组件支持,例如Session管理器,载入器等。

    12.1 StandardContext的配置

    StandardContext实例创建后会调用它的start()方法,但是可能会启动失败,这时它的available属性会被设置为false,该属性表明StandardContext对象是否可用。

    StandardContext类的configured属性是一个布尔变量,表明StandardContext实例是否正确设置。当start()方法被调用时,他会触发一个生命周期事件,事件触发监听器(ContextConfig),对StandardContext实例进行配置。

    12.1.2 启动流程

    1、触发BEFORE_START事件

    2、将available属性设置为false

    3、将configured属性设置为false

    4、配置资源(设置web应用的目录)

    5、设置载入器(setLoder(new WebappLoader()))

    6、设置session管理器(setManger(new Manger()))

    7、初始化字符集映射器

    8、启动与该Context容器相关联的组件

    9、启动子容器

    10、启动管道对象

    11、启动session管理器

    12、触发START事件,此时ContextConfig监听器会对Context进行一些配置操作,如果设置成功就会将configured属性设置为true

    13、检查configured属性的值,如果为true则调用postWelcomePages()方法,载入哪些在启动时就载入的子容器(Wrapper实例),将available属性设置为true。若configured属性为false

    12.1.3 invoke()方法

    该方法由与Context相关的连接器或者父容器(Host)的invoke()方法调用。

    public void invoke(Request request, Response response)
            throws IOException, ServletException {
    
        // 如果正在重载(paused属性为true),则等待
        while (getPaused()) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                ;
            }
        }
    
        // Normal request processing
        if (swallowOutput) {
            try {
                SystemLogHandler.startCapture();
                // 调用父类ContainerBase的invoke()方法
                super.invoke(request, response);
            } finally {
                String log = SystemLogHandler.stopCapture();
                if (log != null && log.length() > 0) {
                    log(log);
                }
            }
        } else {
            super.invoke(request, response);
        }
    
    }
    
    public void invoke(Request request, Response response)
            throws IOException, ServletException {
        pipeline.invoke(request, response);
    }
    

    12.2 映射器

    Tomcat5中映射器已经移除,通过request对象获取Wrapper实例:

    // 是不是解析URI的工作交给了连接器??
    Wrapper wrapper = request.getWrapper();
    

    12.3 对重载的支持

    StandardContext通过reloadable属性来表明应用程序是否启用了重载功能,当启用了重载功能后,web.xml文件发生变化或WEB-INF/classes目录下的其中一个文件被重新编译后,应用程序就会重载。

    StandardContext时通过其载入器实现应用程序的重载的(见8.4)。WebappLoader实现了Runable接口使用另一个线程来不断检查WEB-INF目录中的所有类和JAR文件的时间戳。只需要调用WebappLoader的setContainer()方法将容器和载入器相关联就可以启动该检查线程。

    // WebappLoader.java
    
    public void setContainer(Container container) {
    
        // Deregister from the old Container (if any)
        if ((this.container != null) && (this.container instanceof Context))
            ((Context) this.container).removePropertyChangeListener(this);
    
        // Process this property change
        Container oldContainer = this.container;
        this.container = container;
        support.firePropertyChange("container", oldContainer, this.container);
    
        // Register with the new Container (if any)
        if ((this.container != null) && (this.container instanceof Context)) {
            // 启动检查线程
            setReloadable( ((Context) this.container).getReloadable() );
            ((Context) this.container).addPropertyChangeListener(this);
        }
    
    }
    
    public void setReloadable(boolean reloadable) {
    
        // Process this property change
        boolean oldReloadable = this.reloadable;
        this.reloadable = reloadable;
        support.firePropertyChange("reloadable",
                                    new Boolean(oldReloadable),
                                    new Boolean(this.reloadable));
    
        // Start or stop our background thread if required
        if (!started)
            return;
        // 之前没启动,现在启动了,则启动重载功能
        if (!oldReloadable && this.reloadable)
            // run()方法参见8.4.5
            threadStart();
        // 之前启动了,现在没启动,则关闭
        else if (oldReloadable && !this.reloadable)
            threadStop();
    
    }
    

    12.4 backgroundProcess()方法

    Context容器的运行需要其它组件的支持,例如:载入器、Session管理器。而它们大多都需要执行一个后台任务,Tomcat4中它们都实现了Runable接口,拥有自己的线程。

    在Tomcat5中为了节省资源,所有后台处理共享一个线程。这个共享线程有ContainerBase的start()方法调用threadStart()创建。

    protected void threadStart() {
    
        if (thread != null)
            return;
        if (backgroundProcessorDelay <= 0)
            return;
    
        threadDone = false;
        String threadName = "ContainerBackgroundProcessor[" + toString() + "]";
        thread = new Thread(new ContainerBackgroundProcessor(), threadName);
        thread.setDaemon(true);
        thread.start();
    
    }
    
    
    protected class ContainerBackgroundProcessor implements Runnable {
    
        @Override
        public void run() {
            Throwable t = null;
            String unexpectedDeathMessage = sm.getString(
                    "containerBase.backgroundProcess.unexpectedThreadDeath",
                    Thread.currentThread().getName());
            try {
                while (!threadDone) {
                    try {
                        Thread.sleep(backgroundProcessorDelay * 1000L);
                    } catch (InterruptedException e) {
                        // Ignore
                    }
                    if (!threadDone) {
                        processChildren(ContainerBase.this);
                    }
                }
            } catch (RuntimeException|Error e) {
                t = e;
                throw e;
            } finally {
                if (!threadDone) {
                    log.error(unexpectedDeathMessage, t);
                }
            }
        }
    
        protected void processChildren(Container container) {
            ClassLoader originalClassLoader = null;
    
            try {
                if (container instanceof Context) {
                    Loader loader = ((Context) container).getLoader();
                    // Loader will be null for FailedContext instances
                    if (loader == null) {
                        return;
                    }
    
                    // Ensure background processing for Contexts and Wrappers
                    // is performed under the web app's class loader
                    originalClassLoader = ((Context) container).bind(false, null);
                }
                // 调用容器的后台进程
                container.backgroundProcess();
                Container[] children = container.findChildren();
                for (int i = 0; i < children.length; i++) {
                    if (children[i].getBackgroundProcessorDelay() <= 0) {
                        processChildren(children[i]);
                    }
                }
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                log.error("Exception invoking periodic operation: ", t);
            } finally {
                if (container instanceof Context) {
                    ((Context) container).unbind(false, originalClassLoader);
                }
            }
        }
    }
    
    // StandardContext的backgroundProcess()方法
    @Override
    public void backgroundProcess() {
    
        if (!getState().isAvailable())
            return;
    
        Loader loader = getLoader();
        if (loader != null) {
            try {
                loader.backgroundProcess();
            } catch (Exception e) {
                log.warn(sm.getString(
                        "standardContext.backgroundProcess.loader", loader), e);
            }
        }
        Manager manager = getManager();
        if (manager != null) {
            try {
                manager.backgroundProcess();
            } catch (Exception e) {
                log.warn(sm.getString(
                        "standardContext.backgroundProcess.manager", manager),
                        e);
            }
        }
        WebResourceRoot resources = getResources();
        if (resources != null) {
            try {
                resources.backgroundProcess();
            } catch (Exception e) {
                log.warn(sm.getString(
                        "standardContext.backgroundProcess.resources",
                        resources), e);
            }
        }
        InstanceManager instanceManager = getInstanceManager();
        if (instanceManager instanceof DefaultInstanceManager) {
            try {
                ((DefaultInstanceManager)instanceManager).backgroundProcess();
            } catch (Exception e) {
                log.warn(sm.getString(
                        "standardContext.backgroundProcess.instanceManager",
                        resources), e);
            }
        }
        super.backgroundProcess();
    }
    

    十三、Host和Engine

    如果想要在同一个Tomcat部署多个Context容器的话,就需要使用Host容器。

    13.1 Host接口

    // 用于返回用于处理引入的Http请求的Context容器实例(Tomcat5后已经移除,从request对象中获取)
    Context map(String uri);
    

    13.2 StandardHost

    public class StandardHost
        extends ContainerBase
        implements Deployer, Host {
    
        public StandardHost() {
            super();
            pipeline.setBasic(new StandardHostValve());
        }
    
        public synchronized void start() throws LifecycleException {
            // Set error report valve
            if ((errorReportValveClass != null)
                && (!errorReportValveClass.equals(""))) {
                try {
                    Valve valve = (Valve) Class.forName(errorReportValveClass)
                        .newInstance();
                    // 添加一个ErrorReportValve阀
                    addValve(valve);
                } catch (Throwable t) {
                    log(sm.getString
                        ("standardHost.invalidErrorReportValveClass",
                         errorReportValveClass));
                }
            }
    
            // 添加一个ErrorDispatcherValve阀
            addValve(new ErrorDispatcherValve());
    
            super.start();
        }
    

    后面的放弃了,感觉没啥用...

    十四、服务器组件(Service)和服务组件(Server)

    Server代表着整个tomcat

    Service作用将容器(engine)和连接器组装起来,为外界提供服务

    <Server port="8005" shutdown="SHUTDOWN">
        <Service name="Catalina">
            <Connector port="8080" protocol="HTTP/1.1"
                        connectionTimeout="20000"
                        redirectPort="8443" />
            <!-- Define an AJP 1.3 Connector on port 8009 -->
            <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
            
            <Engine name="Catalina" defaultHost="localhost">
                <Host name="localhost"  appBase="webapps/1"
                    unpackWARs="true" autoDeploy="true">
                </Host>
                <Host name="www.baidu.com"  appBase="webapps/3"
                    unpackWARs="true" autoDeploy="true">
                </Host>
            </Engine>
        </Service>
    </Server>
    

    14.1 服务器组件

    它提供了一种优雅的方法来启动/关闭整个系统,不需要再对连接器和容器分别启动/关闭。

    public interface Server {
        
        // 关闭命令
        String getShutdown();
    
        // 监听的端口号
        int getPort();
    
        // 添加服务组件
        void addService(Service service);
    
        // 删除服务组件
        void removeService(Service service);
    

    14.2 StandardServer类

    StandardServer类有4个与生命周期相关的方法,分别是initialize()方法、start()方法、stop()方法、await()方法。

    调用await()方法后会一直阻塞住,直到8085端口上接收到关闭命令。当await()方法返回后,会运行stop()方法来关闭其下的所有子组件(Catalina的start()方法调用的)。

    public final class StandardServer
        implements Lifecycle, Server {
    

    14.2.1 initialize()方法

    初始化服务器组件

    public void initialize() throws LifecycleException {
        // 使用这个变量防止服务器组件初始化两次
        if (initialized)
            throw new LifecycleException (sm.getString("standardServer.initialize.initialized"));
        initialized = true;
    
        // Initialize our defined Services
        for (int i = 0; i < services.length; i++) {
            // 初始化它下面的服务组件
            services[i].initialize();
        }
    }
    

    14.2.2 start()方法

    启动服务器组件

    public void start() throws LifecycleException {
    
        // Validate and update our current component state
        if (started)
            throw new LifecycleException
                (sm.getString("standardServer.start.started"));
        // Notify our interested LifecycleListeners
        lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
    
        lifecycle.fireLifecycleEvent(START_EVENT, null);
        started = true;
    
        // Start our defined Services
        synchronized (services) {
            for (int i = 0; i < services.length; i++) {
                // 启动它下面的服务组件,服务组件启动时会启动连接器组件和servlet容器
                if (services[i] instanceof Lifecycle)
                    ((Lifecycle) services[i]).start();
            }
        }
    
        // Notify our interested LifecycleListeners
        lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
    
    }
    

    14.2.3 stop()方法

    关闭服务器组件

    public void stop() throws LifecycleException {
    
        // Validate and update our current component state
        if (!started)
            throw new LifecycleException
                (sm.getString("standardServer.stop.notStarted"));
    
        // Notify our interested LifecycleListeners
        lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);
    
        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
        started = false;
    
        // 调用下面的服务组件的stop方法
        for (int i = 0; i < services.length; i++) {
            if (services[i] instanceof Lifecycle)
                ((Lifecycle) services[i]).stop();
        }
    
        // Notify our interested LifecycleListeners
        lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);
    
    }
    

    14.2.4 await()方法

    负责等待关闭整个Tomcat部署的命令

    14.3 Service接口

    一个服务组件可以有一个servlet容器和多个连接器实例。

    14.1 StandardService类

    它的initialize()方法用于初始化添加到其中的所有连接器。

    start()方法负责启动被添加到该服务组件的连接器和servlet容器。

    十五、Digester库

    Digester可以将xml转换成对象。

    15.2 ContextConfig(见12.1)

    与其它容器不同,Context容器必须设置一个监听器来对Context进行配置,设置成功后将configured属性为true。

    工作内容:

    1. 他会为Context的pipline添加一个许可阀和验证器阀;
    2. 读取和解析默认的web.xml和应用自定义的web.xml文件,将xml转换成java对象;
    3. 为每个servlet创建一个StandardWrapper对象。
    public void lifecycleEvent(LifecycleEvent event) {
    
        // Identify the context we are associated with
        try {
            context = (Context) event.getLifecycle();
            if (context instanceof StandardContext) {
                int contextDebug = ((StandardContext) context).getDebug();
                if (contextDebug > this.debug)
                    this.debug = contextDebug;
            }
        } catch (ClassCastException e) {
            log(sm.getString("contextConfig.cce", event.getLifecycle()), e);
            return;
        }
    
        // 处理开始和结束事件
        if (event.getType().equals(Lifecycle.START_EVENT))
            start();
        else if (event.getType().equals(Lifecycle.STOP_EVENT))
            stop();
    
    }
    
    private synchronized void start() {
    
        if (debug > 0)
            log(sm.getString("contextConfig.start"));
        context.setConfigured(false);
        ok = true;
    
        // Set properties based on DefaultContext
        Container container = context.getParent();
        if( !context.getOverride() ) {
            if( container instanceof Host ) {
                // 和DefaultContext有关(它加载了好多的东西,包括listener)
                ((Host)container).importDefaultContext(context);
                container = container.getParent();
            }
            if( container instanceof Engine ) {
                // 和DefaultContext有关(它加载了好多的东西,包括listener)
                ((Engine)container).importDefaultContext(context);
            }
        }
    
        // 读取默认的web.xml
        defaultConfig();
        // 读取应用的web.xml
        applicationConfig();
        if (ok) {
            validateSecurityRoles();
        }
    
        // Scan tag library descriptor files for additional listener classes
        if (ok) {
            try {
                tldScan();
            } catch (Exception e) {
                log(e.getMessage(), e);
                ok = false;
            }
        }
    
        // Configure a certificates exposer valve, if required
        if (ok)
            certificatesConfig();
    
        // Configure an authenticator if we need one
        if (ok)
            authenticatorConfig();
    
        // Dump the contents of this pipeline if requested
        if ((debug >= 1) && (context instanceof ContainerBase)) {
            log("Pipline Configuration:");
            Pipeline pipeline = ((ContainerBase) context).getPipeline();
            Valve valves[] = null;
            if (pipeline != null)
                valves = pipeline.getValves();
            if (valves != null) {
                for (int i = 0; i < valves.length; i++) {
                    log("  " + valves[i].getInfo());
                }
            }
            log("======================");
        }
    
        // Make our application available if no problems were encountered
        if (ok)
            context.setConfigured(true);
        else {
            log(sm.getString("contextConfig.unavailable"));
            context.setConfigured(false);
        }
    
    }
    

    十六、关闭钩子

    Java虚拟机关闭之前会先启动所有的关闭钩子。

    Tomcat利用关闭钩子进行清理工作(调用组件们的stop()方法)

    protected class CatalinaShutdownHook extends Thread {
    
        public void run() {
    
            if (server != null) {
                try {
                    ((Lifecycle) server).stop();
                } catch (LifecycleException e) {
                    System.out.println("Catalina.stop: " + e);
                    e.printStackTrace(System.out);
                    if (e.getThrowable() != null) {
                        System.out.println("----- Root Cause -----");
                        e.getThrowable().printStackTrace(System.out);
                    }
                }
            }
    
        }
    }
    

    十七、启动Tomcat

    Tomcat启动时会用到两个类,分别时Catalina类和Bootstrap类。

    Catalina类用于启动和关闭Server对象,并负责解析Tomcat配置文件server.xml。

    Bootstrap类是一个入口点,负责创建Catalina实例,并调用其process方法。

    17.1 Catalina类

    其中包含一个Digester对象,用于解析server.xml。

    process()方法:

    public void process(String args[]) {
        // 设置连个系统属性,分别时catalina.home和catalina.base.catalina.home
        setCatalinaHome();
        setCatalinaBase();
        try {
            // argument()处理命令行参数
            if (arguments(args))
                // 
                execute();
        } catch (Exception e) {
            e.printStackTrace(System.out);
        }
    }
    
    protected void execute() throws Exception {
        if (starting)
            start();
        else if (stopping)
            stop();
    }
    

    17.1.1 start()方法

    start()方法部分代码:

    // 使用Digester对象解析server.xml
    ...
    
    if (server instanceof Lifecycle) {
        try {
            // 初始化服务器组件
            server.initialize();
            // 启动服务器组件
            ((Lifecycle) server).start();
            try {
                // 注册关闭时的钩子
                Runtime.getRuntime().addShutdownHook(shutdownHook);
            } catch (Throwable t) {
                // This will fail on JDK 1.2. Ignoring, as Tomcat can run
                // fine without the shutdown hook.
            }
            // Wait for the server to be told to shut down
            server.await();
        } catch (LifecycleException e) {
            System.out.println("Catalina.start: " + e);
            e.printStackTrace(System.out);
            if (e.getThrowable() != null) {
                System.out.println("----- Root Cause -----");
                e.getThrowable().printStackTrace(System.out);
            }
        }
    }
    

    17.1.2 stop()方法

    protected void stop() {
    
        // Create and execute our Digester
        Digester digester = createStopDigester();
        File file = configFile();
        try {
            InputSource is =
                new InputSource("file://" + file.getAbsolutePath());
            FileInputStream fis = new FileInputStream(file);
            is.setByteStream(fis);
            digester.push(this);
            digester.parse(is);
            fis.close();
        } catch (Exception e) {
            System.out.println("Catalina.stop: " + e);
            e.printStackTrace(System.out);
            System.exit(1);
        }
    
        // Stop the existing server
        try {
            // 向server组件监听的端口发送SHUTDOWN命令
            Socket socket = new Socket("127.0.0.1", server.getPort());
            OutputStream stream = socket.getOutputStream();
            String shutdown = server.getShutdown();
            for (int i = 0; i < shutdown.length(); i++)
                stream.write(shutdown.charAt(i));
            stream.flush();
            stream.close();
            socket.close();
        } catch (IOException e) {
            System.out.println("Catalina.stop: " + e);
            e.printStackTrace(System.out);
            System.exit(1);
        }
    
    }
    

    Bootstrap主要是创建了3个类加载器,将sharedClassLoader设置给了Catalina的parentClassLoader属性。

    十八、部署器

    要使用一个web应用程序,必须要将该应用程序的Context实例部署到一个Host实例中。在Tomcat中,Context实例可以用war文件的形式来部署。

    部署器是Deployer接口的实例。部署器与Host实例相关联,用来安装Context实例(既创建一个StandardContext实例,并添加到Host实例中)

    Tomcat8中已经找不到它的身影。

    相关文章

      网友评论

          本文标题:How Tomcat Works读书笔记

          本文链接:https://www.haomeiwen.com/subject/sjluvctx.html