TOM源码第一课

/*

  * Licensed to the Apache Software Foundation (ASF) under one or more

  * contributor license agreements.  See the NOTICE file distributed with

  * this work for additional information regarding copyright ownership.

  * The ASF licenses this file to You under the Apache License, Version 2.0

  * (the "License"); you may not use this file except in compliance with

  * the License.  You may obtain a copy of the License at

  * 

  *      http://www.apache.org/licenses/LICENSE-2.0

  * 

  * Unless required by applicable law or agreed to in writing, software

  * distributed under the License is distributed on an "AS IS" BASIS,

  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

  * See the License for the specific language governing permissions and

  * limitations under the License.

  */ 

  

 package javax.servlet;

 import java.io.IOException;

 /**

  * Defines methods that all servlets must implement.

  *

  * <p>A servlet is a small Java program that runs within a Web server.

  * Servlets receive and respond to requests from Web clients,

  * usually across HTTP, the HyperText Transfer Protocol. 

  *

  * <p>To implement this interface, you can write a generic servlet

  * that extends

  * <code>javax.servlet.GenericServlet</code> or an HTTP servlet that

  * extends <code>javax.servlet.http.HttpServlet</code>.

  *

  * <p>This interface defines methods to initialize a servlet,

  * to service requests, and to remove a servlet from the server.

  * These are known as life-cycle methods and are called in the

  * following sequence:

  * <ol>

  * <li>The servlet is constructed, then initialized with the <code>init</code> method.

  * <li>Any calls from clients to the <code>service</code> method are handled.

  * <li>The servlet is taken out of service, then destroyed with the 

  * <code>destroy</code> method, then garbage collected and finalized.

  * </ol>

  *

  * <p>In addition to the life-cycle methods, this interface

  * provides the <code>getServletConfig</code> method, which the servlet 

  * can use to get any startup information, and the <code>getServletInfo</code>

  * method, which allows the servlet to return basic information about itself,

  * such as author, version, and copyright.

  *

  * @author     Various

  * @version     $Version$

  *

  * @see     GenericServlet

  * @see     javax.servlet.http.HttpServlet

  *

  */

 public interface Servlet {

     /**

      * Called by the servlet container to indicate to a servlet that the 

      * servlet is being placed into service.

      *

      * <p>The servlet container calls the <code>init</code>

      * method exactly once after instantiating the servlet.

      * The <code>init</code> method must complete successfully

      * before the servlet can receive any requests.

      *

      * <p>The servlet container cannot place the servlet into service

      * if the <code>init</code> method

      * <ol>

      * <li>Throws a <code>ServletException</code>

      * <li>Does not return within a time period defined by the Web server

      * </ol>

      *

      *

      * @param config            a <code>ServletConfig</code> object 

      *                    containing the servlet's

      *                     configuration and initialization parameters

      *

      * @exception ServletException     if an exception has occurred that

      *                    interferes with the servlet's normal

      *                    operation

      *

      * @see                 UnavailableException

      * @see                 #getServletConfig

      *

      */

     public void init(ServletConfig config) throws ServletException;

     

     

     /**

      *

      * Returns a {@link ServletConfig} object, which contains

      * initialization and startup parameters for this servlet.

      * The <code>ServletConfig</code> object returned is the one 

      * passed to the <code>init</code> method. 

      *

      * <p>Implementations of this interface are responsible for storing the 

      * <code>ServletConfig</code> object so that this 

      * method can return it. The {@link GenericServlet}

      * class, which implements this interface, already does this.

      *

      * @return        the <code>ServletConfig</code> object

      *            that initializes this servlet

      *

      * @see         #init

      *

      */

     public ServletConfig getServletConfig();

     

     

     /**

      * Called by the servlet container to allow the servlet to respond to 

      * a request.

      *

      * <p>This method is only called after the servlet's <code>init()</code>

      * method has completed successfully.

      * 

      * <p>  The status code of the response always should be set for a servlet 

      * that throws or sends an error.

      *

      * 

      * <p>Servlets typically run inside multithreaded servlet containers

      * that can handle multiple requests concurrently. Developers must 

      * be aware to synchronize access to any shared resources such as files,

      * network connections, and as well as the servlet's class and instance 

      * variables. 

      * More information on multithreaded programming in Java is available in 

      * <a href="http://java.sun.com/Series/Tutorial/java/threads/multithreaded.html">

      * the Java tutorial on multi-threaded programming</a>.

      *

      *

      * @param req     the <code>ServletRequest</code> object that contains

      *            the client's request

      *

      * @param res     the <code>ServletResponse</code> object that contains

      *            the servlet's response

      *

      * @exception ServletException     if an exception occurs that interferes

      *                    with the servlet's normal operation 

      *

      * @exception IOException         if an input or output exception occurs

      *

      */

     public void service(ServletRequest req, ServletResponse res)

     throws ServletException, IOException;

     

     

     /**

      * Returns information about the servlet, such

      * as author, version, and copyright.

      * 

      * <p>The string that this method returns should

      * be plain text and not markup of any kind (such as HTML, XML,

      * etc.).

      *

      * @return         a <code>String</code> containing servlet information

      *

      */

     public String getServletInfo();

     

     

     /**

      *

      * Called by the servlet container to indicate to a servlet that the

      * servlet is being taken out of service.  This method is

      * only called once all threads within the servlet's

      * <code>service</code> method have exited or after a timeout

      * period has passed. After the servlet container calls this 

      * method, it will not call the <code>service</code> method again

      * on this servlet.

      *

      * <p>This method gives the servlet an opportunity 

      * to clean up any resources that are being held (for example, memory,

      * file handles, threads) and make sure that any persistent state is

      * synchronized with the servlet's current state in memory.

      *

      */

     public void destroy();

 }

 在Servlet接口中声明了5个方法里,init(),service(),destory()方法是与servlet的生命周期相关的方法,当实例化某个servlet类后,Servlet容器会调用其init()方法进行初始化,至于Servlet容器是如何调用,后面会分析。后面会被类与类之间的衔接来做一定的分析。在servlet接收任何请求之前,必须是经过正确初始化的。一般情况下init()方法可以留空。

 当servlet的一个客户端请求到达后,servlet容器就调用相应的servlet的service()方法,并将javax.serlvet.servletRequest对象和javax.servlet.servletResponse对象作为参数传入,ServletResquest对象包含客户端的HTTP请求的信息,ServletResponse对象则封装servlet的请求信息。这两个类的设计十分有趣,在后面就具体分析这两个类。在servlet对象的整个生命周期内,service()方法会被多次调用。

 在将servlet实例从服务中移除前,servlet容器会调用servlet实例的destory()方法,一般当Servlet容器关闭或servlet容器要释放内存时,才会将servlet实例移除,而且只有当servlet实例的service()方法中的线程都退出或执行超时后,才会调用destory()方法,当servlet容器调用了某个servlet实例的destory()方法后,它就不再调用该servlet实例的service()方法了,调用destory()方法让servlet对象有机会去清理自身持有的资源,如内存,文件句柄和线程等,确保有所的持久化与内存中该servlet对象的当前状态同步。

 下面<how tomcat work>中的一个servlet类:

 import javax.servlet.*;

 import java.io.IOException;

 import java.io.PrintWriter;

 public class PrimitiveServlet implements Servlet {

 public void init(ServletConfig config) throws ServletException {

 System.out.println("init");

 }

 public void service(ServletRequest request, ServletResponse response)

 throws ServletException, IOException {

 System.out.println("from service");

 PrintWriter out = response.getWriter();

 out.println("Hello. Roses are red.");

 out.print("Violets are blue.");

 }

 public void destroy() {

 System.out.println("destroy");

 }

 public String getServletInfo() {

 return null;

 }

 public ServletConfig getServletConfig() {

 return null;

 }

 }

 一个功能齐全的servlet容器有以下几件事要做:

 1,当第一次调用某个servlet,要载入该servlet类,并调用其init()方法(仅此一次);

 2,针对每个request请求,创建一个javax.servlet.ServletRequest实例和一个javax.servlet.ServletResponse实例;

 3,调用该servlet的service()方法,将servletRequest对象和servletResponse对象作为参数传入;

 4,当关闭该servlet类时,调用其destory()方法,并卸载该servlet类。

 那么从客户连接到tomcat上来,最重要要做的事就是如何处理客户的请求并且把请求传递给Servlet容器,虽然看起来很简单,但是这整个过程确是如此的繁杂,包括字符串的解析,以及cookies,session,参数请求等等的工作。那么我们首先的工作就是分析客户连接到tomcat上来这一块,也就是tomcat的默认连接器。

 Tomcat的连接器必须实现org.apache.catalina.Connecor接口,在接口中声明了很多方法,其中最重要的是getContainer(),setContainer(),createRequest(),createResponse()方法。为什么这么说呢?看看以下分析:

 setContainer()方法用于将连接器和某个servlet容器相关联。如果不跟特定的servlet容器关联起来,我靠,你准备要把包装好的HttpRequest和HttpResponse对象传给谁阿??

 getContainer()方法返回与当前连接器相关联的servlet容器。createRequest()方法会引入的HTTP请求创建request对象,相应的,createResponse()方法会创建一个response对象。

 org.apache.catalina.connector.http.HttpConnector类实现了Connector接口,首先大概说以下HttpConnector类做的一些工作:

 1,首先它实现了org.apache.catalina.Connector接口(使其可以成为Catalina中的连接器),又实现了java.lang.Runnble接口(确保它的实例在自己的线程中运行)和实现了org.apache.catalina.Lifecycle接口,Lifecycle接口用于维护每个实现了该接口的每个Catalina组件的生命周期。但是这个接口目前不是重点,在后面会讲到。

 2,由于HttpConnector实现了Lifecycle接口,因此当创建一个HttpConnector实例后,就应该调用其initialize()方法和start()方法,这两个方法如何调用在后面会详细讲到,现在就记住一点:这两个方法只应该被调用一次。

 3,HttpConnector类还负责创建服务器套接字,这是最重要的一点,没有这个,啥事都不用做了。HttpConnector类的initialize()方法会调用一个私有方法open(),后者返回一个java.net.ServerSocket实例,赋值给成员变量serverSocket。但是,这里没有直接调用ServerSocket类的构造函数,而是通过open()方法从一个服务器套接字工厂得到一个实例。

 4,维护HttpProcessor实例:HttpProcessor实例是用来处理HTTP请求的,在Tomcat的默认连接器中,HttpConnector实例有一个HttpProcessor对象池,每个HttpProcessor实例都运行在其自己的线程中。这样HttpConnector实例就可以同时处理多个HTTP请求了。

 好了,接下来就是具体看看它们这几个步骤中tomcat源码的实现:

 首先我们从连接用户开始入手:

 org.apache.catalina.connector.http.HttpConnector

         /**

      * The shutdown signal to our background thread

      */

     private boolean stopped = false;

     

      /**

      * Use TCP no delay ?

      */

     private boolean tcpNoDelay = true;

     

       /**

      * Timeout value on the incoming connection.

      * Note : a value of 0 means no timeout.

      */

     private int connectionTimeout = Constants.DEFAULT_CONNECTION_TIMEOUT;

     

       /**

      * Has this component been started yet?

      */

     private boolean started = false;

     

     

      /**

      * The thread synchronization object.

      */

     private Object threadSync = new Object();

         

         //该方法运行在一个线程中

  // ---------------------------------------------- Background Thread Methods

     /**

      * The background thread that listens for incoming TCP/IP connections and

      * hands them off to an appropriate processor.

      */

     public void run() {

         // Loop until we receive a shutdown command

                //如果tomcat接收到shutdown的命令,那么将会关闭连接器

         while (!stopped) {

             // Accept the next incoming connection from the server socket

             Socket socket = null;

             try {

                 //                if (debug >= 3)

                 //                    log("run: Waiting on serverSocket.accept()");

                 socket = serverSocket.accept();//服务器等待用户的连接

                 //                if (debug >= 3)

                 //                    log("run: Returned from serverSocket.accept()");

                 if (connectionTimeout > 0)

                     socket.setSoTimeout(connectionTimeout);//设置超时时间

                 socket.setTcpNoDelay(tcpNoDelay);//启用/禁用 TCP_NODELAY

             } catch (AccessControlException ace) {

                 log("socket accept security exception", ace);

                 continue;

             } catch (IOException e) {

                 //                if (debug >= 3)

                 //                    log("run: Accept returned IOException", e);

                 try {

                     // If reopening fails, exit

                     synchronized (threadSync) {

                         if (started && !stopped)//如何其他组件还在运行或者socket还没关闭

                             log("accept error: ", e);

                         if (!stopped) {//如果socket还没关闭

                             //                    if (debug >= 3)

                             //                        log("run: Closing server socket");

                             serverSocket.close();//关闭服务器

                             //                        if (debug >= 3)

                             //                            log("run: Reopening server socket");

                             serverSocket = open();//创建新的serverSocket对象

                         }

                     }

                     //                    if (debug >= 3)

                     //                        log("run: IOException processing completed");

                 } catch (IOException ioe) {

                     log("socket reopen, io problem: ", ioe);

                     break;

                 } catch (KeyStoreException kse) {

                     log("socket reopen, keystore problem: ", kse);

                     break;

                 } catch (NoSuchAlgorithmException nsae) {

                     log("socket reopen, keystore algorithm problem: ", nsae);

                     break;

                 } catch (CertificateException ce) {

                     log("socket reopen, certificate problem: ", ce);

                     break;

                 } catch (UnrecoverableKeyException uke) {

                     log("socket reopen, unrecoverable key: ", uke);

                     break;

                 } catch (KeyManagementException kme) {

                     log("socket reopen, key management problem: ", kme);

                     break;

                 }

                 continue;

             }

             // Hand this socket off to an appropriate processor

             HttpProcessor processor = createProcessor();//从HttpProcessor获得一个HttpProcessor对象或者是当池中没有HttpProcessor对象。

             if (processor == null) {

                 try {

                     log(sm.getString("httpConnector.noProcessor"));

                     socket.close();//关闭socket

                 } catch (IOException e) {

                     ;

                 }

                 continue;

             }

             //            if (debug >= 3)

             //                log("run: Assigning socket to processor " + processor);

             processor.assign(socket);//处理HTTP请求

             // The processor will recycle itself when it finishes

         }

         // Notify the threadStop() method that we have shut ourselves down

         //        if (debug >= 3)

         //            log("run: Notifying threadStop() that we have shut down");

         synchronized (threadSync) {

             threadSync.notifyAll();//唤醒有所的线程

         }

     }

     这段代码衍生出来的需要注意的东西有很多,但是先说一点,上面的log()方法是日志记录,这里暂时不说它,后面会详细的说它。好了,现在要从这段代码开始来把握几点需要注意的地方。有些地方需要加上UML来进行解释会比较好!

     

     第一点需要关注的地方:serverSocket = open();我们来看看open()方法做了些什么事情先。首先它是一个私有的方法,也就是用户不能去重写它的方法。

    org.apache.catalina.connector.http.HttpConnector

    

     /**

      * The IP address on which to bind, if any.  If <code>null</code>, all

      * addresses on the server will be bound.

      */

     private String address = null;

     

     

      /**

      * The port number on which we listen for HTTP requests.

      */

     private int port = 8080;

     

     

      /**

      * The accept count for this Connector.

      */

     private int acceptCount = 10;

     

     

     

      private ServerSocket open()

     throws IOException, KeyStoreException, NoSuchAlgorithmException,

            CertificateException, UnrecoverableKeyException,

            KeyManagementException

     {

         // Acquire the server socket factory for this Connector

         ServerSocketFactory factory = getFactory();利用工厂方法来创建一个套接字

         // If no address is specified, open a connection on all addresses

         if (address == null) {

             log(sm.getString("httpConnector.allAddresses"));

             try {

                 return (factory.createSocket(port, acceptCount));//返回创建好了的套接字

             } catch (BindException be) {

                 throw new BindException(be.getMessage() + ":" + port);

             }

         }

         // Open a server socket on the specified address

         try {

             InetAddress is = InetAddress.getByName(address);//在给定主机名的情况下确定主机的IP地址

             log(sm.getString("httpConnector.anAddress", address));

             try {

                 return (factory.createSocket(port, acceptCount, is));

             } catch (BindException be) {

                 throw new BindException(be.getMessage() + ":" + address +

                                         ":" + port);

             }

         } catch (Exception e) {

             log(sm.getString("httpConnector.noAddress", address));

             try {

                 return (factory.createSocket(port, acceptCount));

             } catch (BindException be) {

                 throw new BindException(be.getMessage() + ":" + port);

             }

         }

     }

     看看open()方法:很简单,不多说。主要要加索,保证线程安全

       /**

      * Return the server socket factory used by this Container.

      */

     public ServerSocketFactory getFactory() {

         if (this.factory == null) {

             synchronized (this) {

                 this.factory = new DefaultServerSocketFactory();

             }

         }

         return (this.factory);

     }

     

     

     那么从上面的代码用到了工厂设计模式,具体继续跟踪源码的实现:

     apache.catalina.net.ServerSocketFactory,该类是一个接口,提供了三个方法:

     其实也就是java.net.ServerSocket的三个方法构造,只不过换了以下名字调用而已,它的子类DefaultServerSocketFactory实现了这个接口,并且这个类是fianl类,不可以被集成。具体看看这个类的实现:

     /*

  * Licensed to the Apache Software Foundation (ASF) under one or more

  * contributor license agreements.  See the NOTICE file distributed with

  * this work for additional information regarding copyright ownership.

  * The ASF licenses this file to You under the Apache License, Version 2.0

  * (the "License"); you may not use this file except in compliance with

  * the License.  You may obtain a copy of the License at

  * 

  *      http://www.apache.org/licenses/LICENSE-2.0

  * 

  * Unless required by applicable law or agreed to in writing, software

  * distributed under the License is distributed on an "AS IS" BASIS,

  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

  * See the License for the specific language governing permissions and

  * limitations under the License.

  */

 package org.apache.catalina.net;

 import java.io.IOException;

 import java.net.InetAddress;

 import java.net.ServerSocket;

 import java.security.KeyStoreException;

 import java.security.NoSuchAlgorithmException;

 import java.security.UnrecoverableKeyException;

 import java.security.KeyManagementException;

 import java.security.cert.CertificateException;

 import org.apache.catalina.net.ServerSocketFactory;

 /**

  * Default server socket factory, which returns unadorned server sockts.

  *

  * @author [email protected]

  * @author Harish Prabandham

  * @author Craig R. McClanahan

  */

 public final class DefaultServerSocketFactory implements ServerSocketFactory {

     // --------------------------------------------------------- Public Methods

     /**

      * Returns a server socket which uses all network interfaces on

      * the host, and is bound to a the specified port.  The socket is

      * configured with the socket options (such as accept timeout)

      * given to this factory.

      *

      * @param port the port to listen to

      *

      * @exception IOException                input/output or network error

      * @exception KeyStoreException          error instantiating the

      *                                       KeyStore from file (SSL only)

      * @exception NoSuchAlgorithmException   KeyStore algorithm unsupported

      *                                       by current provider (SSL only)

      * @exception CertificateException       general certificate error (SSL only)

      * @exception UnrecoverableKeyException  internal KeyStore problem with

      *                                       the certificate (SSL only)

      * @exception KeyManagementException     problem in the key management

      *                                       layer (SSL only)

      */

     public ServerSocket createSocket (int port)

     throws IOException, KeyStoreException, NoSuchAlgorithmException,

            CertificateException, UnrecoverableKeyException,

            KeyManagementException {

         return (new ServerSocket(port));

     }

     /**

      * Returns a server socket which uses all network interfaces on

      * the host, is bound to a the specified port, and uses the

      * specified connection backlog.  The socket is configured with

      * the socket options (such as accept timeout) given to this factory.

      *

      * @param port the port to listen to

      * @param backlog how many connections are queued

      *

      * @exception IOException                input/output or network error

      * @exception KeyStoreException          error instantiating the

      *                                       KeyStore from file (SSL only)

      * @exception NoSuchAlgorithmException   KeyStore algorithm unsupported

      *                                       by current provider (SSL only)

      * @exception CertificateException       general certificate error (SSL only)

      * @exception UnrecoverableKeyException  internal KeyStore problem with

      *                                       the certificate (SSL only)

      * @exception KeyManagementException     problem in the key management

      *                                       layer (SSL only)

      */

     public ServerSocket createSocket (int port, int backlog)

     throws IOException, KeyStoreException, NoSuchAlgorithmException,

            CertificateException, UnrecoverableKeyException,

            KeyManagementException {

         return (new ServerSocket(port, backlog));

     }

     /**

      * Returns a server socket which uses only the specified network

      * interface on the local host, is bound to a the specified port,

      * and uses the specified connection backlog.  The socket is configured

      * with the socket options (such as accept timeout) given to this factory.

      *

      * @param port the port to listen to

      * @param backlog how many connections are queued

      * @param ifAddress the network interface address to use

      *

      * @exception IOException                input/output or network error

      * @exception KeyStoreException          error instantiating the

      *                                       KeyStore from file (SSL only)

      * @exception NoSuchAlgorithmException   KeyStore algorithm unsupported

      *                                       by current provider (SSL only)

      * @exception CertificateException       general certificate error (SSL only)

      * @exception UnrecoverableKeyException  internal KeyStore problem with

      *                                       the certificate (SSL only)

      * @exception KeyManagementException     problem in the key management

      *                                       layer (SSL only)

      */

     public ServerSocket createSocket (int port, int backlog,

                                       InetAddress ifAddress)

     throws IOException, KeyStoreException, NoSuchAlgorithmException,

            CertificateException, UnrecoverableKeyException,

            KeyManagementException {

         return (new ServerSocket(port, backlog, ifAddress));

     }

 是吧,是java.nio.ServerSocket类的三个构造方法吧。port端口号,backlog可接收的队列长度,ifAddress要将服务器绑定到的InetAddress

猜你喜欢

转载自cywhoyi.iteye.com/blog/1820618