Tomcat之概述

一级目录

  1. Servlet容器如何工作?
  2. Catalina的设计和实现?
  3. Servlet容器 loadServlet()

3.连接器

以下讨论的并非实际使用的连接器(Coyote)

3.1 连接器作用

解析Http请求的所有信息,交由Servlet实例使用。改为若参数未被servlet实例调用,则默认连接器不会解析。连接器只解析请求头信息,HttpRequest解析请求体或查询字符串中的参数,从而提高效率。

3.2 StringManager/ResourceBundle类

StringManager/ResourceBundle类
错误消息存储在properties文件中,Tomcat创建不同的Properties,StringManager实现了按包单例的模式。
获取错误消息,可以通过sm.getString(key)来获取。

// 可以通过编写properties来获取日志,而非直接编写日志 todo 理解这种好处
if (unloading)
            throw new ServletException
              (sm.getString("standardWrapper.unloading", getName()));

3.3 应用程序

分为连接器模块,启动模块,核心模块
HttpServer==HttpConnector(等待http请求) + HttpProcessor(创建Request/Response对象)
HttpConnector:等待HTTP请求,创建HttpProcessor实例,调用HttpProcessor的processor()方法
启动模块:Bootstrap
连接器模块:1.连接器及其支持类(HttpConnector/HttpProcessor)
2.HttpRequest/HttpResponse/HttpRequestFacade/HttpResponseFacade/
3.常量类
HttpConnector类:
实现了Runnable接口:run方法中有以下三步操作:

3.4 Tomcat4中默认连接器

3.4.1导读

对连接器的要求:

  • 实现Connector接口
  • 创建Request/Response对象
    工作原理:
    等待HTTP请求,创建Request/Response对象,调用Container invoke()方法,将request,response传给servlet容器
    Tomcat4中的默认连接器支持HTTP1.1,HTTP0.9,HTTP1.0
3.4.2 HTTP1.1的新特性

HTTP各版本特性

3.4.3 Connector接口

具有get/setContainer(),createRequest/Response()方法

3.4.4 HttpConnector类

创建ServerSocket,维护HttpProcessor池,提供Http请求服务

  1. 创建ServerSocket
 private ServerSocket open(){
 	// 获取factory
 	ServerSocketFactory factory = getFactory();
 	// 创建ServerSocket
 	return factory.createSocket();
 }
 public ServerSocketFactory getFactory() {
        if (this.factory == null) {
            synchronized (this) {
                this.factory = new DefaultServerSocketFactory();
            }
        }
        return (this.factory);
    }
  1. 维护HttpProcessor池
    此处细看todo
    3.提供Http请求服务
    通过run方法
    通过createProcessor()获得一个HttpProcessor对象

3.5 HttpProcessor类

HttpProcessor类有拥有run()方法,每个HttpProcessor可以运行在自己的线程(处理器线程)
当BootStrap调用容器线程,修改available为true,后调用assign方法,唤醒HttpProcess中的await()方法继续执行。

private synchronized Socket await() {

        // Wait for the Connector to provide a new Socket
        while (!available) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }

        // Notify the Connector that we have received this Socket
        //采取局部变量,是为了在执行结束后,继续接受下一个socket
        Socket socket = this.socket;
        available = false;
        notifyAll();

        if ((debug >= 1) && (socket != null))
            log("  The incoming request has been awaited");

        return (socket);

    }

3.6 处理请求

HttpConnector将收到的请求转化为Socket,并将Socket传给processor的assign方法,assign通过设置available=true且唤醒await方法,将run()方法继续执行。run()方法中调用process()方法将创建request/response,并且解析Http的请求头等。

4 Container接口

容器必须实现Container接口,Tomcat共有四种容器:Engine,Host,Context,Wrapper,其标准实现均为:StandardXXX,四个标准实现均包含抽象类ContainerBase.在部署应用时,可通过编辑server.xml决定使用何种容器。

4.1 管道任务

管道(Pipeline):包含servlet容器所要执行的具体任务
阀(Valve):一个任务就是一个阀
可通过编辑server.xml动态添加阀,每个容器都有一个基础阀(基础阀最后执行)。
当调用容器的invoke()方法后,容器将处理工作交由管道完成。
整体流程:容器调用HttpProcessor.process()方法,其中执行容器的invoke()方法,容器的invoke()方法将调用管道pipeline.invoke(),管道invoke()将调用

   (new StandardPipelineValveContext()).invokeNext(request, response);

从而执行invokeNext()方法

// subscript为初始值
// stage比subscript大1;
public void invokeNext(Request request, Response response)
            throws IOException, ServletException {
            int subscript = stage;
            stage = stage + 1;
            // Invoke the requested Valve for the current request thread
            if (subscript < valves.length) {
                valves[subscript].invoke(request, response, this);
            } else if ((subscript == valves.length) && (basic != null)) {
                basic.invoke(request, response, this);
            } else {
                throw new ServletException
                    (sm.getString("standardPipeline.noValve"));
            }

        }
4.1.1 Pipeline接口

Pipeline共有以下六种方法
Pipeline接口方法
其中addValve(Valve valve)方法实现细节如下

	public void addValve(Valve valve) {
        // Validate that we can add this Valve
        if (valve instanceof Contained)
            ((Contained) valve).setContainer(this.container);
        // Start the new component if necessary
        if (started && (valve instanceof Lifecycle)) {
            try {
                ((Lifecycle) valve).start();
            } catch (LifecycleException e) {
                log("StandardPipeline.addValve: start: ", e);
            }
        }
        // Add this Valve to the set associated with this Pipeline
        synchronized (valves) {
            Valve results[] = new Valve[valves.length +1];
            System.arraycopy(valves, 0, results, 0, valves.length);
            results[valves.length] = valve;
            valves = results;
        }
    }
4.1.2 Valve接口

其中getInfo()用来表明自己的属性。
在这里插入图片描述

4.1.3 ValveContext接口

此接口主要用来表示Valve上下文关系,主要有getInfo()和invokeNext()方法
问题:管道中如何保证Valve从第一个执行到最后一个

4.1.4 Contained接口

通过此方法可以实现至多与一个servlet容器相关联。

4.2 Wrapper接口

主要方法load():初始化一个Servlet
allocate():分配一个已经初始化的servlet
todo:SingleThreadModel(第11节讲述)
load()/allocate()两个方法均会调用:loadServlet()方法获得servlet
扩展阅读:todo loadServlet()方法详解

4.3 Context接口

主要方法addWrapper(),createWrapper()方法
todo instanceListeners是干什么的
instanceListeners:监听器的类名数组

    public Wrapper createWrapper() {
        Wrapper wrapper = new StandardWrapper();
        synchronized (instanceListeners) {
            for (int i = 0; i < instanceListeners.length; i++) {
                try {
                    Class clazz = Class.forName(instanceListeners[i]);
                    InstanceListener listener =
                            (InstanceListener) clazz.newInstance();
                    // 创建了一个监听器
                    wrapper.addInstanceListener(listener);
                } catch (Throwable t) {
                    log("createWrapper", t);
                    return (null);
                }
            }
        }

4.4 其它知识

基础阀:不调用ValveContext接口的invokeNext()方法,因为是基础阀
同时,Valve的invoke()中将调用servlet.service()方法;注意是Wrapper实例的基础阀调用servlet.service()方法,而不是Wrapper实例。

4.5 Context应用程序

有多个servlet类时,使用映射器

todo 映射器学一下:映射器其实使用HashMap存储(key,value):(protocol,container)的

5. Lifecycle

生命周期:众多组件随Catalina启动和关闭,这些组件需实现Lifecycle接口。父组件负责启动/关闭它的子组件。

5.1 Lifecycle接口

Lifecycle接口方法和变量

5.2 LifecycleEvent类

LifecycleEvent:生命周期事件

5.3 LifecycleListener接口

生命周期监听器:包含lifecycleEvent()方法。
扩展阅读:监听器模式

5.4 LifecycleSupport类

LifecycleSupport是Tomcat提供的生命周期支持类
Lifecycle方法和变量

public final class LifecycleSupport {
		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);

    }

}

在Lifecycle所管理的组件中,有

 	/**
     * The lifecycle event support for this component.
     */
    protected LifecycleSupport lifecycle = new LifecycleSupport(this);
然后调用lifecycle.fireLifecycleEvent()触发事件

6 日志系统

Tomcat中代表日志的接口Logger
Logger变量和方法
日志系统主要有LoggerBase类,LoggerBase类未实现log(String):void方法。其FileLogger,SystemErrLogger,SystemOutLoggger三个类均继承LoggerBase类。其中带有verbosity形参的log方法,只有当this.verbosity>=verbosity时才会调用log(String):void

// LoggerBase的两个子类
public class SystemOutLogger
    extends LoggerBase {
    protected static final String info =
        "org.apache.catalina.logger.SystemOutLogger/1.0";
    public void log(String msg) {
        System.out.println(msg);
    }
}

public class SystemErrLogger
    extends LoggerBase {
    protected static final String info =
        "org.apache.catalina.logger.SystemErrLogger/1.0";
    public void log(String msg) {
        System.err.println(msg);
    }
}

public class FileLogger{
    public void log(String msg) {
        // Construct the timestamp we will use, if requested
        Timestamp ts = new Timestamp(System.currentTimeMillis());
        String tsString = ts.toString().substring(0, 19);
        String tsDate = tsString.substring(0, 10);

        // If the date has changed, switch log files
        if (!date.equals(tsDate)) {
            synchronized (this) {
                if (!date.equals(tsDate)) {
                    close();
                    date = tsDate;
                    open();
                }
            }
        }    
        // Log this message, timestamped if necessary
        if (writer != null) {
            if (timestamp) {
                writer.println(tsString + " " + msg);
            } else {
                writer.println(msg);
            }
        }

    }
    private void close() {

        if (writer == null)
            return;
        writer.flush();
        writer.close();
        writer = null;
        date = "";

    }
    */
    private void open() {
        // Create the directory if necessary
        File dir = new File(directory);
        if (!dir.isAbsolute())
            dir = new File(System.getProperty("catalina.base"), directory);
        dir.mkdirs();
        // Open the current log file
        try {
            String pathname = dir.getAbsolutePath() + File.separator +
                prefix + date + suffix;
            writer = new PrintWriter(new FileWriter(pathname, true), true);
        } catch (IOException e) {
            writer = null;
        }
    }
}

7 载入器

出于安全的考虑,Servlet容器不应信任Servlet类,只让其访问WEB-INF/classes的类,且提供自动重载的功能。若要支持自动重载,需实现Loader接口。
自定义类载入器:载入类中指定规则,缓存已经存在的类,实现类的预载入。

7.1 类载入器

三种类载入器:Bootstrap(启动jvm,java核心类),extension(加入标准扩展目录中的类),system class loader(CLASSPATH)

7.2 Loader接口

Loader接口定义
WebappLoader类作为Loader的实现,WebappClassLoader类作为其类载入器。

7.3 Reload接口

Reloader定义
如果Web应用程序的某个servlet类被修改则返回true

7.4 WebappLoader类

实现了Runnable,不断的调用modified(),查找是否修改。

 public void run() {

        if (debug >= 1)
            log("BACKGROUND THREAD Starting");

        // 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;
            }

            // Handle a need for reloading
            notifyContext();
            break;

        }

        if (debug >= 1)
            log("BACKGROUND THREAD Stopping");

    }
7.4.1 创建类载入器

可以通过setLoaderClass(String):void方法填写自定义的类载入器。但是自定义类载入器需要继承WebappClassLoader,否则会报错。

private WebappClassLoader createClassLoader()
        throws Exception {
        Class clazz = Class.forName(loaderClass);
        WebappClassLoader classLoader = null;
        if (parentClassLoader == null) {
            // Will cause a ClassCast is the class does not extend WCL, but
            // this is on purpose (the exception will be caught and rethrown)
            classLoader = (WebappClassLoader) clazz.newInstance();
        } else {
            Class[] argTypes = { ClassLoader.class };
            Object[] args = { parentClassLoader };
            Constructor constr = clazz.getConstructor(argTypes);
            classLoader = (WebappClassLoader) constr.newInstance(args);
        }
        return classLoader;
    }
7.4.2 设置仓库

通过setRepository()将仓库添加到类载入器。

7.4.3 设置类路径

setClassPath()

7.4.4 设置访问权限

setPermissions()

7.4.5 开启新线程执行类的重载

当modified()==true时,执行notifyContext()方法执行通知·

	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;
            }
            // Handle a need for reloading
            notifyContext();
            break;
        }
    // 
    private void notifyContext() {
        WebappContextNotifier notifier = new WebappContextNotifier();
        // 开一个新线程
        (new Thread(notifier)).start();
    }
    // WebappContextNotifier为内部类
    protected class WebappContextNotifier implements Runnable {
        /**
         * Perform the requested notification.
         */
        public void run() {
            ((Context) container).reload();
        }
    }

7.5 WebappClassLoader类

WebappClassLoader会缓存之前已经载入的类和加载失败的类的名字,当再次请求加载失败的类时,会直接报错。
triggers代表不允许载入的类,packageTriggers代表不 允许载入的包和子包。

    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
    };
7.5.1 类缓存
	// 缓存的类(已经加载的类)
    protected HashMap resourceEntries = new HashMap();
	// 载入失败的类
	protected HashMap notFoundResources = new HashMap();

8 Session

8.1 Manager

Manger是Session管理器,负责管理Session对象。Manager有一个工具类:ManagerBase,其有两个直接子类:StandardManager,PerisistentManagerBase。

8.1.1 Manager接口

Manager接口定义
setContainer()用于将Manager与指定容器关联,load()用于将对象从辅助存储器加载到内存,unload()相反。

8.1.2 ManagerBase类

使用createSession()创建新的session,add(),remove()等增加和移除session。

    public Session createSession() {
        // Recycle or create a Session instance
        Session session = createEmptySession();
        // Initialize the properties of the new session and return it
        session.setNew(true);
        session.setValid(true);
        session.setCreationTime(System.currentTimeMillis());
        session.setMaxInactiveInterval(this.maxInactiveInterval);
        String sessionId = generateSessionId();
        String jvmRoute = getJvmRoute();
        // @todo Move appending of jvmRoute generateSessionId()???
        if (jvmRoute != null) {
            sessionId += '.' + jvmRoute;
        }
        synchronized (sessions) {
            while (sessions.get(sessionId) != null){ // Guarantee uniqueness
                sessionId = generateSessionId();
                duplicates++;
                // @todo Move appending of jvmRoute generateSessionId()???
                if (jvmRoute != null) {
                    sessionId += '.' + jvmRoute;
                }
            }
        }
        session.setId(sessionId);
        sessionCounter++;
        return (session);
    }
     // todo 看源码
     public Session createEmptySession() {
        Session session = null;
        synchronized (recycled) {
            int size = recycled.size();
            if (size > 0) {
                session = (Session) recycled.get(size - 1);
                recycled.remove(size - 1);
            }
        }
        if (session != null)
            session.setManager(this);
        else
            session = new StandardSession(this);
        return(session);
    }
    // todo 扩展阅读吧
    protected synchronized String generateSessionId() {
        // Generate a byte array containing a session identifier
        Random random = getRandom();
        byte bytes[] = new byte[SESSION_ID_BYTES];
        getRandom().nextBytes(bytes);
        bytes = getDigest().digest(bytes);
        // Render the result as a String of hexadecimal digits
        StringBuffer result = new StringBuffer();
        for (int i = 0; i < bytes.length; i++) {
            byte b1 = (byte) ((bytes[i] & 0xf0) >> 4);
            byte b2 = (byte) (bytes[i] & 0x0f);
            if (b1 < 10)
                result.append((char) ('0' + b1));
            else
                result.append((char) ('A' + (b1 - 10)));
            if (b2 < 10)
                result.append((char) ('0' + b2));
            else
                result.append((char) ('A' + (b2 - 10)));
        }
        return (result.toString());
    }

8.1.2 PersistentManagerBase类

PersistentManagerBase是所有持久化Session管理器的父类,PersistentManagerBase使用store的私有引用。

	// PersistentManagerBase使用专门的线程处理备份和换出Session对象的任务。
    public void run() {
        // Loop until the termination semaphore is set
        while (!threadDone) {
            threadSleep();
            processExpires();
            processPersistenceChecks();
        }
    }
    // 1.1 过期session的处理
     protected void processExpires() {
        if (!started)
            return;
        long timeNow = System.currentTimeMillis();
        Session sessions[] = findSessions();
        for (int i = 0; i < sessions.length; i++) {
            StandardSession session = (StandardSession) sessions[i];
            if (!session.isValid())
                continue;
            if (isSessionStale(session, timeNow))
                session.expire();
        }
    }
    // 1.2 注意此处用空闲时间>=活跃时间判断是否过期
    // FIXME: Probably belongs in the Session class.
    protected boolean isSessionStale(Session session, long timeNow) {
        int maxInactiveInterval = session.getMaxInactiveInterval();
        if (maxInactiveInterval >= 0) {
            int timeIdle = // Truncate, do not round up
                (int) ((timeNow - session.getLastAccessedTime()) / 1000L);
            if (timeIdle >= maxInactiveInterval)
                return true;
        }
        return false;
    }
    //
    public void processPersistenceChecks() {
            // 空闲的交换
            processMaxIdleSwaps();
            processMaxActiveSwaps();
            // 备份
            processMaxIdleBackups();

    }

查找session findSession(String id):Session

	// 
    public Session findSession(String id) throws IOException {
		// 先从内存中查找
        Session session = super.findSession(id);
        if (session != null)
            return (session);
            
        // See if the Session is in the Store
        // 从存储器中查找
        session = swapIn(id);
        return (session);
    }

8.1.3 PersistentManager类

PersistentManager类继承PersistentManagerBase,多加了两个属性。

8.1.4 DistributedManager类

8.2 Store存储器

Store定义

// save:用于将指定的session对象存储到持久性存储器中
// load:从存储器中将session对象载入内存
8.2.1 StoreBase类
public abstract class StoreBase
    implements Lifecycle, Runnable, Store {}
StoreBase中并未实现save,load方法。

FileStore类会将Session对象存储到某个文件中,文件名后缀加.session

8.2.2 JDBCStore类

将Session对象通过JDBC存入数据库。

9.安全性

todo

10.StandardWrapper

  1. 连接器创建request/response对象
  2. 调用StandardContext invoke()方法
  3. StandardContext invoke()方法——>调用基阀的invoke()
  4. 基础阀invoke()调用Wrapper invoke()处理HTTP请求
  5. 调用管道的invoke,最后油基础阀调用invoke()方法,使用allocate()获取servlet实例
  6. load()调用servlet实例的init()方法
  7. StandardWrapperValve service()方法

10.1载入Servlet类

	public synchronized void load() throws ServletException {
        instance = loadServlet();
    }
    /**
    1. 先判断是否已经加载过此类
    2. 没加载过则设置类名
    3. 获得加载器
    4. 获得servlet
    5. 返回servlet
    */
        public synchronized Servlet loadServlet() throws ServletException {

        // Nothing to do if we already have an instance or an instance pool
        // 此前已经加载过,则直接返回
        if (!singleThreadModel && (instance != null))
            return instance;
        PrintStream out = System.out;
        if (swallowOutput) {
            SystemLogHandler.startCapture();
        }
        Servlet servlet = null;
        try {
            // If this "servlet" is really a JSP file, get the right class.
            // HOLD YOUR NOSE - this is a kludge that avoids having to do special
            // case Catalina-specific code in Jasper - it also requires that the
            // servlet path be replaced by the <jsp-file> element content in
            // order to be completely effective
            String actualClass = servletClass;
            if ((actualClass == null) && (jspFile != null)) {
                Wrapper jspWrapper = (Wrapper)
                    ((Context) getParent()).findChild(Constants.JSP_SERVLET_NAME);
                if (jspWrapper != null)
                    actualClass = jspWrapper.getServletClass();
            }

            // Complain if no servlet class has been specified
            if (actualClass == null) {
                unavailable(null);
                throw new ServletException
                    (sm.getString("standardWrapper.notClass", getName()));
            }
    		// 3.获得加载器
            // Acquire an instance of the class loader to be used
            Loader loader = getLoader();
            if (loader == null) {
                unavailable(null);
                throw new ServletException
                    (sm.getString("standardWrapper.missingLoader", getName()));
            }
    		// 4.获得类加载器
            ClassLoader classLoader = loader.getClassLoader();
    
            // Special case class loader for a container provided servlet
            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) {
                    classClass = classLoader.loadClass(actualClass);
                } else {
                    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
            // 获得servlet
            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);
            }
    
            // Check if loading the servlet in this web application should be 
            // allowed
            if (!isServletAllowed(servlet)) {
                throw new SecurityException
                    (sm.getString("standardWrapper.privilegedServlet", 
                                  actualClass));
            }
    
            // Special handling for ContainerServlet instances
            if ((servlet instanceof ContainerServlet) &&
                isContainerProvidedServlet(actualClass)) {
                ((ContainerServlet) servlet).setWrapper(this);
            }
    
    
            // Call the initialization method of this servlet
            try {
                instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT,
                                                  servlet);
                servlet.init(facade);
                // Invoke jspInit on JSP pages
                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);
                }
                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);
            }
    
            // Register our newly initialized instance
            singleThreadModel = servlet instanceof SingleThreadModel;
            if (singleThreadModel) {
                if (instancePool == null)
                    instancePool = new Stack();
            }
            fireContainerEvent("load", this);
        } finally {
            if (swallowOutput) {
                String log = SystemLogHandler.stopCapture();
                if (log != null && log.length() > 0) {
                    if (getServletContext() != null) {
                        getServletContext().log(log);
                    } else {
                        out.println(log);
                    }
                }
            }
        }
        return servlet;

    }
   

10.2 ServletConfig对象

StandardWrapper获取ServletConfig对象
ServletConfig定义
初始化参数
通过getInitParameter(String)方法,获取初始化参数,存储在一个HashMap中

10.3 servlet容器的父子关系

Wrapper的父类是Context,所以Wrapper setparent()方法只能传Context容器

10.4 StandWrapperFacade类

11 Server类

本节研究关闭/打开servlet容器
服务器组件:可以启动/关闭整个系统,不需要对连接器和容器分别启动/关闭。
服务器组件启动后会启动所有的组件,然后等待关闭命令。
shutdown属性保存关闭命令,port属性定义了端口号。

11.1 StandardServer类

可以通过addservice(),removeService()/findService()方法的实现。

11.1.1 三大方法 initialize() start() stop()
	// initialized防止初始化两次
 	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();
        }
    }
    // 启动所有的服务组件
    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++) {
                if (services[i] instanceof Lifecycle)
                    ((Lifecycle) services[i]).start();
            }
        }

        // Notify our interested LifecycleListeners
        lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);

    }

	/**
	1. 创建ServerSocket对象,监听8005端口
	2. 获取socket
	3. 将消息与关闭命令字符串比较,相同跳出while循环,关闭SocketServer
	*/
 	public void await() {
        // Set up a server socket to wait on
        ServerSocket serverSocket = null;
        try {
            serverSocket =
                new ServerSocket(port, 1,
                                 InetAddress.getByName("127.0.0.1"));
        } catch (IOException e) {
            System.err.println("StandardServer.await: create[" + port
                               + "]: " + e);
            e.printStackTrace();
            System.exit(1);
        }

        // Loop waiting for a connection and a valid command
        while (true) {

            // Wait for the next connection
            Socket socket = null;
            InputStream stream = null;
            try {
                socket = serverSocket.accept();
                socket.setSoTimeout(10 * 1000);  // Ten seconds
                stream = socket.getInputStream();
            } catch (AccessControlException ace) {
                System.err.println("StandardServer.accept security exception: "
                                   + ace.getMessage());
                continue;
            } catch (IOException e) {
                System.err.println("StandardServer.await: accept: " + e);
                e.printStackTrace();
                System.exit(1);
            }

            // Read a set of characters from the socket
            StringBuffer command = new StringBuffer();
            int expected = 1024; // Cut off to avoid DoS attack
            while (expected < shutdown.length()) {
                if (random == null)
                    random = new Random(System.currentTimeMillis());
                expected += (random.nextInt() % 1024);
            }
            while (expected > 0) {
                int ch = -1;
                try {
                    ch = stream.read();
                } catch (IOException e) {
                    System.err.println("StandardServer.await: read: " + e);
                    e.printStackTrace();
                    ch = -1;
                }
                if (ch < 32)  // Control character or EOF terminates loop
                    break;
                command.append((char) ch);
                expected--;
            }

            // Close the socket now that we are done with it
            try {
                socket.close();
            } catch (IOException e) {
                ;
            }

            // Match against our command string
            boolean match = command.toString().equals(shutdown);
            if (match) {
                break;
            } else
                System.err.println("StandardServer.await: Invalid command '" +
                                   command.toString() + "' received");

        }

        // Close the server socket and return
        try {
            serverSocket.close();
        } catch (IOException e) {
            ;
        }

    }

11.2 Service接口

服务组件是Service接口的实例,一个服务组件可以有一个容器和多个连接器
Service定义

11.2.1 StandardService类

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

  1. connector/container
    只会有一个container和多个connector,
 	// 多个连接器
    private Connector connectors[] = new Connector[0];
	// 一个容器
    private Container container = null;

 	public void setContainer(Container container) {
        Container oldContainer = this.container;
        if ((oldContainer != null) && (oldContainer instanceof Engine))
            ((Engine) oldContainer).setService(null);
        this.container = container;
        if ((this.container != null) && (this.container instanceof Engine))
            ((Engine) this.container).setService(this);
        if (started && (this.container != null) &&
            (this.container instanceof Lifecycle)) {
            try {
                ((Lifecycle) this.container).start();
            } catch (LifecycleException e) {
                ;
            }
        }
        synchronized (connectors) {
            for (int i = 0; i < connectors.length; i++)
                connectors[i].setContainer(this.container);
        }
        if (started && (oldContainer != null) &&
            (oldContainer instanceof Lifecycle)) {
            try {
                ((Lifecycle) oldContainer).stop();
            } catch (LifecycleException e) {
                ;
            }
        }
        // Report this property change to interested listeners
        support.firePropertyChange("container", oldContainer, this.container);
    }
    public void addConnector(Connector connector) {
        synchronized (connectors) {
            connector.setContainer(this.container);
            connector.setService(this);
            Connector results[] = new Connector[connectors.length + 1];
            System.arraycopy(connectors, 0, results, 0, connectors.length);
            results[connectors.length] = connector;
            connectors = results;
            if (initialized) {
                try {
                    connector.initialize();
                } catch (LifecycleException e) {
                    e.printStackTrace(System.err);
                }
            }
            if (started && (connector instanceof Lifecycle)) {
                try {
                    ((Lifecycle) connector).start();
                } catch (LifecycleException e) {
                    ;
                }
            }
            // Report this property change to interested listeners
            support.firePropertyChange("connector", null, connector);
        }

    }
  1. 与生命周期有关的方法
    initialize()用于初始化
    start()负责启动被添加的容器和连接器
    stop()用于关闭
  2. Stopper类
    以优雅的方式关闭servlet容器。

12关闭钩子

关闭钩子是java提供的用于在jvm推出后执行代码清理工作的技术
主要实现如下

public class ShutdownHook {
    public static void main(String[] args) {
    	//2. 在适合的地方开启关闭钩子
        new ShutdownHook().start();
        System.out.println("正常程序");
        System.exit(0);
    }
    
    public void start() {
    	// 1.注册关闭钩子
        Runtime.getRuntime().addShutdownHook(new Thread((new Runnable() {
            public void run() {
                System.out.println("关闭钩子运行");
            }
        })));
    }
}

关闭钩子使用场景:

  1. 程序正常退出
  2. 使用System.exit()
  3. 终端使用Ctrl+C触发的中断
  4. 系统关闭
  5. OutOfMemory宕机
  6. 使用Kill pid命令干掉进程(注:在使用kill -9 pid时,是不会被调用的)

Tomcat中关闭钩子的使用

public class Catalina{
	public void start(){
		Thread shutdownHook = new CatalinaShutdownHook();
        Runtime.getRuntime().addShutdownHook(shutdownHook);
       }
    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);
                    }
                }
            }
            
        }

    }
}

13 启动Tomcat

启动tomcat主要依赖Bootstrap和Catalina
Catalina负责启动/关闭Server对象,解析Tomcal的server.xml配置文件。

13.1 Catalina

Catalina定义如下,主要方法process(String[]):void
Catalina定义

 	/**
     * The instance main program.
     *
     * @param args Command line arguments
     */
    public void process(String args[]) {
        setCatalinaHome();//设置catalina.home的值 user.dir
        setCatalinaBase();//设置catalina.base.catalina.home的值 user.dir
        try {
            if (arguments(args))
                execute();
        } catch (Exception e) {
            e.printStackTrace(System.out);
        }
    }
    /**
     * Execute the processing that has been configured from the command line.
     */
    protected void execute() throws Exception {
        if (starting)
            start();
        else if (stopping)
            stop();

    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值